up
This commit is contained in:
+82
-3
@@ -87,6 +87,15 @@ class WhatsAppBotManager {
|
||||
});
|
||||
}
|
||||
|
||||
// Contador de caracteres para broadcast
|
||||
const broadcastMessage = document.getElementById('broadcast-message');
|
||||
const charCount = document.getElementById('char-count');
|
||||
if (broadcastMessage && charCount) {
|
||||
broadcastMessage.addEventListener('input', (e) => {
|
||||
charCount.textContent = e.target.value.length;
|
||||
});
|
||||
}
|
||||
|
||||
// Cambio de tipo de mensaje
|
||||
const messageType = document.getElementById('message-type');
|
||||
if (messageType) {
|
||||
@@ -695,9 +704,79 @@ class WhatsAppBotManager {
|
||||
}
|
||||
|
||||
async sendBroadcast() {
|
||||
// Funcionalidad de envío masivo deshabilitada temporalmente desde UI
|
||||
this.showError('Envío masivo deshabilitado temporalmente');
|
||||
return;
|
||||
const selectionType = document.getElementById('broadcast-selection-type').value;
|
||||
const messageType = document.getElementById('broadcast-message-type').value;
|
||||
|
||||
let payload = {
|
||||
selection_type: selectionType,
|
||||
message_type: messageType
|
||||
};
|
||||
|
||||
// Validar según tipo de selección
|
||||
if (selectionType === 'filter') {
|
||||
payload.filter = document.getElementById('broadcast-filter').value;
|
||||
} else {
|
||||
const selectedUsers = $('#broadcast-users').val();
|
||||
if (!selectedUsers || selectedUsers.length === 0) {
|
||||
this.showError('Debes seleccionar al menos un usuario');
|
||||
return;
|
||||
}
|
||||
payload.user_ids = selectedUsers;
|
||||
}
|
||||
|
||||
// Validar según tipo de mensaje
|
||||
if (messageType === 'text') {
|
||||
const message = document.getElementById('broadcast-message').value;
|
||||
if (!message.trim()) {
|
||||
this.showError('El mensaje no puede estar vacío');
|
||||
return;
|
||||
}
|
||||
payload.message = message;
|
||||
} else {
|
||||
const templateId = document.getElementById('broadcast-template').value;
|
||||
if (!templateId) {
|
||||
this.showError('Debes seleccionar una plantilla');
|
||||
return;
|
||||
}
|
||||
|
||||
payload.template_id = templateId;
|
||||
|
||||
// Recoger variables
|
||||
const variables = {};
|
||||
document.querySelectorAll('.broadcast-template-var').forEach(input => {
|
||||
variables[input.dataset.var] = input.value;
|
||||
});
|
||||
payload.template_variables = variables;
|
||||
}
|
||||
|
||||
// Confirmar envío
|
||||
const targetText = selectionType === 'filter'
|
||||
? `usuarios filtrados por "${payload.filter}"`
|
||||
: `${payload.user_ids.length} usuario(s) seleccionado(s)`;
|
||||
const messageText = messageType === 'text' ? 'mensaje de texto' : 'plantilla';
|
||||
|
||||
if (!confirm(`¿Enviar este ${messageText} a ${targetText}?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.showLoading('Enviando mensajes masivos...');
|
||||
const result = await this.apiCall('send_broadcast.php', 'POST', payload);
|
||||
|
||||
if (result.success) {
|
||||
this.showSuccess(`✅ Mensajes enviados exitosamente!\nEnviados: ${result.sent_count}\nErrores: ${result.error_count}\nTotal usuarios: ${result.total_users}`);
|
||||
document.getElementById('broadcast-form').reset();
|
||||
$('#broadcast-users').val(null).trigger('change');
|
||||
document.getElementById('broadcast-template-variables').style.display = 'none';
|
||||
document.getElementById('broadcast-template-preview').style.display = 'none';
|
||||
} else {
|
||||
this.showError(result.error || 'Error enviando broadcast');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('Error enviando broadcast: ' + error.message);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
async saveMenu() {
|
||||
|
||||
+752
-32
@@ -112,12 +112,21 @@ class SimpleWhatsAppManager {
|
||||
case 'users':
|
||||
this.loadUsers();
|
||||
break;
|
||||
case 'admin_users':
|
||||
this.loadAdminUsers();
|
||||
break;
|
||||
case 'send_message':
|
||||
this.loadconversations();
|
||||
break;
|
||||
case 'templates':
|
||||
this.loadTemplates();
|
||||
break;
|
||||
case 'broadcast':
|
||||
// Recargar plantillas cuando se abre el tab de broadcast
|
||||
if (typeof loadBroadcastTemplates === 'function') {
|
||||
loadBroadcastTemplates();
|
||||
}
|
||||
break;
|
||||
case 'menus':
|
||||
this.loadMenus();
|
||||
break;
|
||||
@@ -403,14 +412,19 @@ class SimpleWhatsAppManager {
|
||||
}
|
||||
}
|
||||
|
||||
async loadUsers() {
|
||||
this.log('Cargando usuarios');
|
||||
async loadUsers(page = 1, search = '') {
|
||||
this.log('Cargando usuarios - página ' + page);
|
||||
|
||||
try {
|
||||
const response = await this.apiCall('get_users.php');
|
||||
let url = `get_users.php?page=${page}&limit=20`;
|
||||
if (search) {
|
||||
url += `&search=${encodeURIComponent(search)}`;
|
||||
}
|
||||
|
||||
const response = await this.apiCall(url);
|
||||
|
||||
if (response && response.success) {
|
||||
this.updateUsersList(response.data || []);
|
||||
this.updateUsersList(response.data || [], response.pagination || {});
|
||||
} else {
|
||||
this.showError('No se pudieron cargar los usuarios');
|
||||
}
|
||||
@@ -420,15 +434,17 @@ class SimpleWhatsAppManager {
|
||||
}
|
||||
}
|
||||
|
||||
updateUsersList(users) {
|
||||
updateUsersList(users, pagination = {}) {
|
||||
const container = document.getElementById('users-table');
|
||||
if (!container) return;
|
||||
|
||||
// Guardar datos para uso posterior
|
||||
this.usersData = users;
|
||||
this.usersPagination = pagination;
|
||||
|
||||
if (users.length === 0) {
|
||||
container.innerHTML = '<tr><td colspan="7" class="text-center text-muted">No hay usuarios registrados</td></tr>';
|
||||
this.updateUsersPagination({});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -459,6 +475,75 @@ class SimpleWhatsAppManager {
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
this.updateUsersPagination(pagination);
|
||||
}
|
||||
|
||||
updateUsersPagination(pagination) {
|
||||
const paginationContainer = document.getElementById('users-pagination');
|
||||
if (!paginationContainer) return;
|
||||
|
||||
if (!pagination.totalPages || pagination.totalPages <= 1) {
|
||||
paginationContainer.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPage = pagination.page || 1;
|
||||
const totalPages = pagination.totalPages;
|
||||
const total = pagination.total || 0;
|
||||
|
||||
let html = `
|
||||
<div class="d-flex justify-content-between align-items-center mt-3">
|
||||
<div class="text-muted">
|
||||
Mostrando página ${currentPage} de ${totalPages} (${total} usuarios en total)
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="pagination pagination-sm mb-0">
|
||||
<li class="page-item ${!pagination.hasPrev ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="window.whatsappManager.loadUsers(1); return false;">
|
||||
<i class="fas fa-angle-double-left"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item ${!pagination.hasPrev ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="window.whatsappManager.loadUsers(${currentPage - 1}); return false;">
|
||||
<i class="fas fa-angle-left"></i>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
|
||||
// Mostrar páginas alrededor de la actual
|
||||
const maxPages = 5;
|
||||
let startPage = Math.max(1, currentPage - Math.floor(maxPages / 2));
|
||||
let endPage = Math.min(totalPages, startPage + maxPages - 1);
|
||||
|
||||
if (endPage - startPage < maxPages - 1) {
|
||||
startPage = Math.max(1, endPage - maxPages + 1);
|
||||
}
|
||||
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
html += `
|
||||
<li class="page-item ${i === currentPage ? 'active' : ''}">
|
||||
<a class="page-link" href="#" onclick="window.whatsappManager.loadUsers(${i}); return false;">${i}</a>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
|
||||
html += `
|
||||
<li class="page-item ${!pagination.hasNext ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="window.whatsappManager.loadUsers(${currentPage + 1}); return false;">
|
||||
<i class="fas fa-angle-right"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item ${!pagination.hasNext ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="window.whatsappManager.loadUsers(${totalPages}); return false;">
|
||||
<i class="fas fa-angle-double-right"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
`;
|
||||
|
||||
paginationContainer.innerHTML = html;
|
||||
}
|
||||
|
||||
async loadConversations() {
|
||||
@@ -716,6 +801,7 @@ class SimpleWhatsAppManager {
|
||||
const fields = [
|
||||
{ id: 'whatsapp-token', value: data.whatsapp_token },
|
||||
{ id: 'phone-number-id', value: data.phone_number_id },
|
||||
{ id: 'business-account-id', value: data.business_account_id },
|
||||
{ id: 'webhook-token', value: data.webhook_verify_token },
|
||||
{ id: 'api-url', value: data.api_url },
|
||||
{ id: 'business-name', value: data.business_name },
|
||||
@@ -739,6 +825,7 @@ class SimpleWhatsAppManager {
|
||||
const system_config = {
|
||||
token: document.getElementById('whatsapp-token').value,
|
||||
phone_number_id: document.getElementById('phone-number-id').value,
|
||||
business_account_id: document.getElementById('business-account-id').value,
|
||||
webhook_verify_token: document.getElementById('webhook-token').value,
|
||||
api_url: document.getElementById('api-url').value,
|
||||
business_name: document.getElementById('business-name').value,
|
||||
@@ -831,6 +918,9 @@ class SimpleWhatsAppManager {
|
||||
}
|
||||
|
||||
this.log(`Usuarios cargados: ${users.length}, Plantillas cargadas: ${templates.length}`);
|
||||
|
||||
// Guardar plantillas para uso posterior
|
||||
this.templatesData = templates;
|
||||
|
||||
if (users.length > 0) {
|
||||
this.populateUsersSelect(users);
|
||||
@@ -875,13 +965,15 @@ class SimpleWhatsAppManager {
|
||||
// Limpiar opciones existentes (excepto la primera)
|
||||
select.innerHTML = '<option value="">Seleccionar plantilla...</option>';
|
||||
|
||||
// Agregar plantillas
|
||||
templates.forEach(template => {
|
||||
const option = document.createElement('option');
|
||||
option.value = template.template_name;
|
||||
option.textContent = `${template.name} - ${template.category || 'Sin descripción'}`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
// Agregar plantillas (solo las aprobadas)
|
||||
templates
|
||||
.filter(template => template.status && template.status.toUpperCase() === 'APPROVED')
|
||||
.forEach(template => {
|
||||
const option = document.createElement('option');
|
||||
option.value = template.id; // Usar ID en lugar de template_name
|
||||
option.textContent = `${template.name} - ${template.category || 'Sin descripción'}`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
async loadTemplates() {
|
||||
@@ -1367,38 +1459,90 @@ class SimpleWhatsAppManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare template payload; support named parameters (e.g. { name: 'Juan' })
|
||||
const tplName = templateSelect.value;
|
||||
let tplParams = null;
|
||||
// Obtener ID y nombre de la plantilla
|
||||
const templateId = templateSelect.value;
|
||||
const selectedOption = templateSelect.options[templateSelect.selectedIndex];
|
||||
|
||||
// Buscar la plantilla en templatesData para obtener el template_name
|
||||
let templateName = null;
|
||||
if (this.templatesData) {
|
||||
const template = this.templatesData.find(t => t.id == templateId);
|
||||
if (template) {
|
||||
templateName = template.template_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!templateName) {
|
||||
this.showError('No se pudo obtener el nombre de la plantilla');
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's a hidden input with JSON parameters (index.php), use it
|
||||
// Recopilar variables de los inputs
|
||||
let tplParams = {};
|
||||
const varInputs = document.querySelectorAll('.message-template-var');
|
||||
|
||||
if (varInputs.length > 0) {
|
||||
console.log(`🔍 Recopilando ${varInputs.length} variables de la plantilla`);
|
||||
|
||||
// Detectar si las variables son numéricas o con nombres
|
||||
let hasNumericVars = false;
|
||||
let hasNamedVars = false;
|
||||
|
||||
varInputs.forEach(input => {
|
||||
const placeholder = input.dataset.placeholder || '';
|
||||
if (/^\{\{\d+\}\}$/.test(placeholder)) {
|
||||
hasNumericVars = true;
|
||||
} else {
|
||||
hasNamedVars = true;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`📊 Tipo de variables detectado: ${hasNumericVars ? 'numéricas' : 'con nombres'}`);
|
||||
|
||||
if (hasNumericVars && !hasNamedVars) {
|
||||
// Variables numéricas: crear array ordenado por índice
|
||||
const tempObj = {};
|
||||
varInputs.forEach(input => {
|
||||
const index = parseInt(input.dataset.var);
|
||||
tempObj[index] = input.value || '';
|
||||
});
|
||||
// Convertir a array ordenado
|
||||
const sortedKeys = Object.keys(tempObj).map(Number).sort((a, b) => a - b);
|
||||
tplParams = sortedKeys.map(key => tempObj[key]);
|
||||
console.log('📊 Enviando como array ordenado:', tplParams);
|
||||
} else {
|
||||
// Variables con nombres: crear objeto
|
||||
varInputs.forEach(input => {
|
||||
const placeholder = input.dataset.placeholder || '';
|
||||
// Extraer nombre de la variable: {{nombre_tema}} -> nombre_tema
|
||||
const varName = placeholder.replace(/\{\{|\}\}/g, '');
|
||||
tplParams[varName] = input.value || '';
|
||||
});
|
||||
console.log('📦 Enviando como objeto con nombres:', tplParams);
|
||||
}
|
||||
} else {
|
||||
console.log('ℹ️ Plantilla sin variables');
|
||||
}
|
||||
|
||||
// Si hay input oculto con parámetros (para compatibilidad), usarlo
|
||||
const paramsInput = document.getElementById('template-params');
|
||||
if (paramsInput && paramsInput.value) {
|
||||
try {
|
||||
tplParams = JSON.parse(paramsInput.value);
|
||||
const hiddenParams = JSON.parse(paramsInput.value);
|
||||
if (Object.keys(hiddenParams).length > 0) {
|
||||
tplParams = hiddenParams;
|
||||
console.log('📝 Usando parámetros del input oculto:', tplParams);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Invalid JSON in #template-params, ignoring', e);
|
||||
tplParams = null;
|
||||
}
|
||||
}
|
||||
|
||||
// If no params provided and template is contacto_nuevo, prompt operator for name
|
||||
if ((!tplParams || (typeof tplParams === 'object' && Object.keys(tplParams).length === 0)) && tplName === 'contacto_nuevo') {
|
||||
const defaultName = '';
|
||||
const inputName = prompt('Ingrese el nombre para la plantilla contacto_nuevo (dejar vacío para usar el número):', defaultName);
|
||||
if (inputName === null) {
|
||||
// user cancelled
|
||||
return;
|
||||
}
|
||||
const clean = (typeof inputName === 'string') ? inputName.trim() : '';
|
||||
tplParams = { name: clean || recipient };
|
||||
}
|
||||
|
||||
messageData = {
|
||||
recipient: recipient,
|
||||
type: 'template',
|
||||
template_name: tplName,
|
||||
parameters: tplParams || {}
|
||||
template_name: templateName, // Usar template_name en lugar de ID
|
||||
parameters: tplParams
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1651,6 +1795,164 @@ class SimpleWhatsAppManager {
|
||||
this.showError('Error eliminando plantilla: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async sendBroadcast() {
|
||||
const selectionType = document.getElementById('broadcast-selection-type').value;
|
||||
const messageType = document.getElementById('broadcast-message-type').value;
|
||||
|
||||
let payload = {
|
||||
selection_type: selectionType,
|
||||
message_type: messageType
|
||||
};
|
||||
|
||||
// Validar según tipo de selección
|
||||
if (selectionType === 'filter') {
|
||||
payload.filter = document.getElementById('broadcast-filter').value;
|
||||
} else {
|
||||
const selectedUsers = $('#broadcast-users').val();
|
||||
if (!selectedUsers || selectedUsers.length === 0) {
|
||||
this.showError('Debes seleccionar al menos un usuario');
|
||||
return;
|
||||
}
|
||||
payload.user_ids = selectedUsers;
|
||||
}
|
||||
|
||||
// Validar según tipo de mensaje
|
||||
if (messageType === 'text') {
|
||||
const message = document.getElementById('broadcast-message').value;
|
||||
if (!message.trim()) {
|
||||
this.showError('El mensaje no puede estar vacío');
|
||||
return;
|
||||
}
|
||||
payload.message = message;
|
||||
} else {
|
||||
const templateId = document.getElementById('broadcast-template').value;
|
||||
if (!templateId) {
|
||||
this.showError('Debes seleccionar una plantilla');
|
||||
return;
|
||||
}
|
||||
|
||||
payload.template_id = templateId;
|
||||
|
||||
// Recoger y validar variables
|
||||
const variablesInputs = document.querySelectorAll('.broadcast-template-var');
|
||||
let hasEmpty = false;
|
||||
|
||||
// Detectar si las variables son numéricas o con nombres usando el placeholder
|
||||
const firstInput = variablesInputs[0];
|
||||
const placeholder = firstInput?.dataset?.placeholder || '';
|
||||
// Si el placeholder es {{1}}, {{2}}, etc. -> numéricas
|
||||
// Si es {{nombre_tema}}, {{fecha}}, etc. -> con nombres
|
||||
const isNumericVar = /^\{\{\d+\}\}$/.test(placeholder);
|
||||
|
||||
console.log('🔍 Detectando tipo de variables:');
|
||||
console.log(' - Placeholder ejemplo:', placeholder);
|
||||
console.log(' - Es numérica?', isNumericVar);
|
||||
|
||||
let variables;
|
||||
if (isNumericVar) {
|
||||
// Variables numéricas {{1}}, {{2}} - usar array ordenado
|
||||
const tempVars = {};
|
||||
variablesInputs.forEach(input => {
|
||||
const varIndex = parseInt(input.dataset.var);
|
||||
if (!input.value.trim()) {
|
||||
input.classList.add('is-invalid');
|
||||
hasEmpty = true;
|
||||
} else {
|
||||
input.classList.remove('is-invalid');
|
||||
tempVars[varIndex] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasEmpty) {
|
||||
this.showError('Por favor completa todos los campos de variables de la plantilla');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convertir a array ordenado por índice
|
||||
variables = [];
|
||||
const sortedKeys = Object.keys(tempVars).map(k => parseInt(k)).sort((a, b) => a - b);
|
||||
sortedKeys.forEach(key => {
|
||||
variables.push(tempVars[key]);
|
||||
});
|
||||
|
||||
console.log('📝 Variables numéricas recopiladas:', tempVars);
|
||||
console.log('📊 Array ordenado:', variables);
|
||||
} else {
|
||||
// Variables con nombres {{fecha}}, {{motivo}} - usar objeto
|
||||
variables = {};
|
||||
variablesInputs.forEach(input => {
|
||||
const placeholder = input.dataset.placeholder || '';
|
||||
const varName = placeholder.replace(/\{\{|\}\}/g, ''); // Extraer nombre de {{fecha}}
|
||||
|
||||
if (!input.value.trim()) {
|
||||
input.classList.add('is-invalid');
|
||||
hasEmpty = true;
|
||||
} else {
|
||||
input.classList.remove('is-invalid');
|
||||
variables[varName] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasEmpty) {
|
||||
this.showError('Por favor completa todos los campos de variables de la plantilla');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('📝 Variables con nombres recopiladas:', variables);
|
||||
}
|
||||
|
||||
payload.template_variables = variables;
|
||||
}
|
||||
|
||||
// Confirmar envío
|
||||
const targetText = selectionType === 'filter'
|
||||
? `usuarios filtrados por "${payload.filter}"`
|
||||
: `${payload.user_ids.length} usuario(s) seleccionado(s)`;
|
||||
const messageText = messageType === 'text' ? 'mensaje de texto' : 'plantilla';
|
||||
|
||||
if (!confirm(`¿Enviar este ${messageText} a ${targetText}?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.showInfo('Enviando mensajes masivos...');
|
||||
const result = await this.apiCall('send_broadcast.php', {
|
||||
method: 'POST',
|
||||
body: payload
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
this.showSuccess(`✅ Mensajes enviados exitosamente!\nEnviados: ${result.sent_count}\nErrores: ${result.error_count}\nTotal usuarios: ${result.total_users}`);
|
||||
document.getElementById('broadcast-form').reset();
|
||||
$('#broadcast-users').val(null).trigger('change');
|
||||
document.getElementById('broadcast-template-variables').style.display = 'none';
|
||||
document.getElementById('broadcast-template-preview').style.display = 'none';
|
||||
} else {
|
||||
this.showError(result.error || 'Error enviando broadcast');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('Error enviando broadcast: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Cargar usuarios administradores del sistema
|
||||
async loadAdminUsers() {
|
||||
this.log('Cargando usuarios administradores...');
|
||||
|
||||
try {
|
||||
const response = await this.apiCall('list_admin_users.php');
|
||||
|
||||
if (response && response.success && response.data) {
|
||||
renderAdminUsersTable(response.data);
|
||||
} else {
|
||||
this.showError('Error cargando usuarios administradores');
|
||||
}
|
||||
} catch (error) {
|
||||
this.log('Error cargando usuarios administradores: ' + error.message, 'error');
|
||||
this.showError('Error cargando usuarios: ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializar cuando el DOM esté listo
|
||||
@@ -2839,9 +3141,121 @@ window.toggleMessageType = function () {
|
||||
} else {
|
||||
textGroup.style.display = 'block';
|
||||
templateGroup.style.display = 'none';
|
||||
// Ocultar variables y preview cuando no es plantilla
|
||||
const varsContainer = document.getElementById('message-template-variables');
|
||||
const previewContainer = document.getElementById('message-template-preview');
|
||||
if (varsContainer) varsContainer.style.display = 'none';
|
||||
if (previewContainer) previewContainer.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
// Cargar detalles de plantilla para send_message
|
||||
window.loadMessageTemplateDetails = async function() {
|
||||
console.log('🔵 loadMessageTemplateDetails llamado');
|
||||
const select = document.getElementById('message-template');
|
||||
const templateId = select.value;
|
||||
console.log('Template ID seleccionado:', templateId);
|
||||
|
||||
if (!templateId) {
|
||||
document.getElementById('message-template-variables').style.display = 'none';
|
||||
document.getElementById('message-template-preview').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.whatsappManager.apiCall(`get_template_details.php?id=${templateId}`);
|
||||
console.log('✅ Detalles de plantilla:', response);
|
||||
|
||||
if (response && response.success) {
|
||||
const template = response.template || response.data;
|
||||
if (!template) {
|
||||
console.error('❌ No se encontró template en la respuesta');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('📋 Template data:', template);
|
||||
console.log('📋 Variables:', template.variables);
|
||||
|
||||
const variablesContainer = document.getElementById('message-template-variables');
|
||||
|
||||
if (template.variables && template.variables.length > 0) {
|
||||
console.log(`✅ Se encontraron ${template.variables.length} variables`);
|
||||
let html = '<label class="form-label fw-bold"><i class="fas fa-edit"></i> Variables de la Plantilla</label>';
|
||||
|
||||
template.variables.forEach((variable, index) => {
|
||||
const varLabel = variable.label || variable.name || `Variable ${variable.index || index + 1}`;
|
||||
const placeholder = variable.placeholder || `{{${variable.index || index + 1}}}`;
|
||||
const example = variable.example || '';
|
||||
|
||||
html += `
|
||||
<div class="mb-2">
|
||||
<label class="form-label small text-muted">${varLabel}</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control form-control-sm message-template-var"
|
||||
data-var="${variable.index || index + 1}"
|
||||
data-placeholder="${placeholder}"
|
||||
placeholder="${example || 'Ingrese ' + varLabel.toLowerCase()}"
|
||||
oninput="updateMessagePreview()"
|
||||
>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
console.log('📝 HTML generado para variables:', html);
|
||||
variablesContainer.innerHTML = html;
|
||||
variablesContainer.style.display = 'block';
|
||||
console.log('✅ Variables container mostrado');
|
||||
} else {
|
||||
console.log('⚠️ Template sin variables');
|
||||
variablesContainer.innerHTML = '<p class="text-muted small">Esta plantilla no tiene variables</p>';
|
||||
variablesContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
updateMessagePreview();
|
||||
} else {
|
||||
console.error('❌ Respuesta sin success:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error cargando detalles de plantilla:', error);
|
||||
window.whatsappManager.showError('Error cargando plantilla');
|
||||
}
|
||||
};
|
||||
|
||||
// Actualizar preview de plantilla para send_message
|
||||
window.updateMessagePreview = async function() {
|
||||
const templateId = document.getElementById('message-template').value;
|
||||
if (!templateId) return;
|
||||
|
||||
// Recolectar variables como array (por índice)
|
||||
const variablesArray = [];
|
||||
document.querySelectorAll('.message-template-var').forEach(input => {
|
||||
const index = parseInt(input.dataset.var) - 1; // Convertir a índice de array (0-based)
|
||||
variablesArray[index] = input.value || `{{${input.dataset.var}}}`;
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await window.whatsappManager.apiCall('preview_template.php', {
|
||||
method: 'POST',
|
||||
body: { template_id: templateId, parameters: variablesArray }
|
||||
});
|
||||
|
||||
if (response && response.success && response.preview) {
|
||||
const previewContainer = document.getElementById('message-template-preview');
|
||||
const previewContent = document.getElementById('message-preview-content');
|
||||
|
||||
// Usar el HTML pre-formateado del servidor
|
||||
previewContent.innerHTML = response.preview.html || response.preview.body.replace(/\\n/g, '<br>');
|
||||
previewContainer.style.display = 'block';
|
||||
} else {
|
||||
console.error('Error en preview:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error generando vista previa:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Función de diagnóstico
|
||||
window.runDiagnostic = function () {
|
||||
const resultsDiv = document.getElementById('diagnostic-results');
|
||||
@@ -4113,6 +4527,90 @@ window.exportUsers = function() {
|
||||
}
|
||||
};
|
||||
|
||||
// Función para buscar usuarios con debounce
|
||||
let searchUsersTimeout;
|
||||
window.searchUsers = function() {
|
||||
clearTimeout(searchUsersTimeout);
|
||||
searchUsersTimeout = setTimeout(() => {
|
||||
const searchInput = document.getElementById('users-search');
|
||||
const searchTerm = searchInput ? searchInput.value.trim() : '';
|
||||
|
||||
if (window.whatsappManager && window.whatsappManager.loadUsers) {
|
||||
window.whatsappManager.loadUsers(1, searchTerm);
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// Función para sincronizar plantillas desde Facebook
|
||||
window.syncTemplatesFromFacebook = async function() {
|
||||
console.log('Sincronizando plantillas desde Facebook...');
|
||||
|
||||
if (!window.whatsappManager) {
|
||||
alert('Error: Sistema no inicializado');
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = document.getElementById('btn-sync-templates');
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sincronizando...';
|
||||
}
|
||||
|
||||
try {
|
||||
window.whatsappManager.showInfo('Sincronizando plantillas desde Facebook/WhatsApp Business...');
|
||||
|
||||
const response = await window.whatsappManager.apiCall('sync_templates_from_facebook.php', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
const data = response.data || {};
|
||||
const message = `
|
||||
✅ Sincronización completada exitosamente:
|
||||
• ${data.synced || 0} plantillas nuevas
|
||||
• ${data.updated || 0} plantillas actualizadas
|
||||
• ${data.skipped || 0} sin cambios
|
||||
${data.errors && data.errors.length > 0 ? '\n⚠️ ' + data.errors.length + ' errores encontrados' : ''}
|
||||
`;
|
||||
|
||||
window.whatsappManager.showSuccess(message);
|
||||
|
||||
// Recargar la lista de plantillas
|
||||
if (window.whatsappManager.loadTemplates) {
|
||||
window.whatsappManager.loadTemplates();
|
||||
}
|
||||
|
||||
// Mostrar errores si existen
|
||||
if (data.errors && data.errors.length > 0) {
|
||||
console.warn('Errores durante la sincronización:', data.errors);
|
||||
}
|
||||
} else {
|
||||
const errorMsg = response?.error || 'Error desconocido al sincronizar';
|
||||
window.whatsappManager.showError(`Error al sincronizar plantillas: ${errorMsg}`);
|
||||
|
||||
// Si el error es por falta de WABA ID, dar instrucciones
|
||||
if (errorMsg.includes('Business Account ID')) {
|
||||
setTimeout(() => {
|
||||
alert('💡 Para sincronizar plantillas necesitas configurar el Business Account ID (WABA ID).\n\n' +
|
||||
'1. Ve a la pestaña "Configuración"\n' +
|
||||
'2. Ingresa tu WABA ID en el campo correspondiente\n' +
|
||||
'3. Guarda la configuración\n' +
|
||||
'4. Vuelve a intentar la sincronización\n\n' +
|
||||
'Puedes encontrar tu WABA ID en Meta Business Manager.');
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sincronizando plantillas:', error);
|
||||
window.whatsappManager.showError('Error de conexión al sincronizar plantillas: ' + error.message);
|
||||
} finally {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-sync-alt"></i> Sincronizar desde Facebook';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Función para refrescar logs
|
||||
window.refreshLogs = function() {
|
||||
console.log('Refrescando logs...');
|
||||
@@ -4189,4 +4687,226 @@ window.clearLogs = async function() {
|
||||
} else {
|
||||
alert('Error: Sistema no inicializado');
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// GESTIÓN DE USUARIOS ADMINISTRADORES
|
||||
// ============================================
|
||||
|
||||
// Renderizar tabla de usuarios administradores
|
||||
function renderAdminUsersTable(users) {
|
||||
const tbody = document.getElementById('admin-users-table');
|
||||
if (!tbody) return;
|
||||
|
||||
if (!users || users.length === 0) {
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted">
|
||||
<i class="fas fa-inbox"></i> No hay usuarios registrados
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = users.map(user => {
|
||||
const isActive = parseInt(user.is_active) === 1;
|
||||
const lastLogin = user.last_login ?
|
||||
new Date(user.last_login).toLocaleString('es-CO') :
|
||||
'<span class="text-muted">Nunca</span>';
|
||||
const createdAt = new Date(user.created_at).toLocaleDateString('es-CO');
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td><strong>${escapeHtml(user.username)}</strong></td>
|
||||
<td>${escapeHtml(user.full_name || '-')}</td>
|
||||
<td>${escapeHtml(user.email || '-')}</td>
|
||||
<td>
|
||||
<span class="badge bg-${isActive ? 'success' : 'secondary'}">
|
||||
${isActive ? 'Activo' : 'Inactivo'}
|
||||
</span>
|
||||
</td>
|
||||
<td><small>${lastLogin}</small></td>
|
||||
<td><small>${createdAt}</small></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editAdminUser(${user.id})"
|
||||
title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning"
|
||||
onclick="changeAdminPassword(${user.id}, '${escapeHtml(user.username)}')"
|
||||
title="Cambiar contraseña">
|
||||
<i class="fas fa-key"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-${isActive ? 'secondary' : 'success'}"
|
||||
onclick="toggleAdminUserStatus(${user.id}, ${!isActive})"
|
||||
title="${isActive ? 'Desactivar' : 'Activar'}">
|
||||
<i class="fas fa-${isActive ? 'ban' : 'check'}"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// Mostrar modal para crear usuario
|
||||
window.showCreateAdminUserModal = function() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('adminUserModal'));
|
||||
document.getElementById('admin-user-modal-title').textContent = 'Nuevo Usuario';
|
||||
document.getElementById('admin-user-form').reset();
|
||||
document.getElementById('admin-user-id').value = '';
|
||||
document.getElementById('password-group').style.display = 'block';
|
||||
modal.show();
|
||||
};
|
||||
|
||||
// Editar usuario existente
|
||||
window.editAdminUser = async function(userId) {
|
||||
try {
|
||||
const response = await window.whatsappManager.apiCall('list_admin_users.php');
|
||||
|
||||
if (response && response.success && response.data) {
|
||||
const user = response.data.find(u => parseInt(u.id) === parseInt(userId));
|
||||
|
||||
if (user) {
|
||||
document.getElementById('admin-user-modal-title').textContent = 'Editar Usuario';
|
||||
document.getElementById('admin-user-id').value = user.id;
|
||||
document.getElementById('admin-username').value = user.username;
|
||||
document.getElementById('admin-fullname').value = user.full_name || '';
|
||||
document.getElementById('admin-email').value = user.email || '';
|
||||
document.getElementById('password-group').style.display = 'none';
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('adminUserModal'));
|
||||
modal.show();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error cargando usuario:', error);
|
||||
alert('Error al cargar datos del usuario');
|
||||
}
|
||||
};
|
||||
|
||||
// Guardar usuario (crear o actualizar)
|
||||
window.saveAdminUser = async function() {
|
||||
const userId = document.getElementById('admin-user-id').value;
|
||||
const username = document.getElementById('admin-username').value.trim();
|
||||
const fullName = document.getElementById('admin-fullname').value.trim();
|
||||
const email = document.getElementById('admin-email').value.trim();
|
||||
const password = document.getElementById('admin-password').value;
|
||||
|
||||
if (!username) {
|
||||
alert('El nombre de usuario es requerido');
|
||||
return;
|
||||
}
|
||||
|
||||
// Si es nuevo usuario, validar contraseña
|
||||
if (!userId && !password) {
|
||||
alert('La contraseña es requerida para usuarios nuevos');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId && password.length < 6) {
|
||||
alert('La contraseña debe tener al menos 6 caracteres');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let response;
|
||||
|
||||
if (userId) {
|
||||
// Actualizar usuario existente
|
||||
response = await window.whatsappManager.apiCall('update_admin_user.php', {
|
||||
method: 'POST',
|
||||
body: { user_id: userId, username, full_name: fullName, email }
|
||||
});
|
||||
} else {
|
||||
// Crear nuevo usuario
|
||||
response = await window.whatsappManager.apiCall('create_admin_user.php', {
|
||||
method: 'POST',
|
||||
body: { username, full_name: fullName, email, password }
|
||||
});
|
||||
}
|
||||
|
||||
if (response && response.success) {
|
||||
window.whatsappManager.showSuccess(response.message || 'Usuario guardado correctamente');
|
||||
bootstrap.Modal.getInstance(document.getElementById('adminUserModal')).hide();
|
||||
window.whatsappManager.loadAdminUsers();
|
||||
} else {
|
||||
alert(response?.error || 'Error al guardar usuario');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error guardando usuario:', error);
|
||||
alert('Error al guardar usuario: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// Cambiar contraseña de usuario
|
||||
window.changeAdminPassword = function(userId, username) {
|
||||
document.getElementById('change-password-user-id').value = userId;
|
||||
document.getElementById('change-password-username').textContent = username;
|
||||
document.getElementById('new-password').value = '';
|
||||
document.getElementById('confirm-password').value = '';
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('changePasswordModal'));
|
||||
modal.show();
|
||||
};
|
||||
|
||||
// Guardar nueva contraseña
|
||||
window.saveNewPassword = async function() {
|
||||
const userId = document.getElementById('change-password-user-id').value;
|
||||
const newPassword = document.getElementById('new-password').value;
|
||||
const confirmPassword = document.getElementById('confirm-password').value;
|
||||
|
||||
if (!newPassword || newPassword.length < 6) {
|
||||
alert('La contraseña debe tener al menos 6 caracteres');
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert('Las contraseñas no coinciden');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.whatsappManager.apiCall('change_admin_password.php', {
|
||||
method: 'POST',
|
||||
body: { user_id: userId, new_password: newPassword }
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
window.whatsappManager.showSuccess('Contraseña actualizada correctamente');
|
||||
bootstrap.Modal.getInstance(document.getElementById('changePasswordModal')).hide();
|
||||
} else {
|
||||
alert(response?.error || 'Error al cambiar contraseña');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error cambiando contraseña:', error);
|
||||
alert('Error al cambiar contraseña: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// Activar/Desactivar usuario
|
||||
window.toggleAdminUserStatus = async function(userId, isActive) {
|
||||
const action = isActive ? 'activar' : 'desactivar';
|
||||
|
||||
if (!confirm(`¿Está seguro de ${action} este usuario?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.whatsappManager.apiCall('toggle_admin_user_status.php', {
|
||||
method: 'POST',
|
||||
body: { user_id: userId, is_active: isActive }
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
window.whatsappManager.showSuccess(`Usuario ${isActive ? 'activado' : 'desactivado'} correctamente`);
|
||||
window.whatsappManager.loadAdminUsers();
|
||||
} else {
|
||||
alert(response?.error || `Error al ${action} usuario`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error ${action}ando usuario:`, error);
|
||||
alert(`Error al ${action} usuario: ` + error.message);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user