501 lines
21 KiB
PHP
501 lines
21 KiB
PHP
<?php
|
|
session_start();
|
|
require_once 'config/config.php';
|
|
|
|
// Verificar autenticación
|
|
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
|
header('Location: login.php');
|
|
exit;
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>📅 Mensajes Programados - WhatsApp Bot</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1400px;
|
|
}
|
|
|
|
.card {
|
|
border-radius: 15px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
border: none;
|
|
}
|
|
|
|
.scheduled-message-card {
|
|
margin-bottom: 15px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.scheduled-message-card:hover {
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
.status-badge {
|
|
font-size: 0.85rem;
|
|
padding: 5px 12px;
|
|
}
|
|
|
|
.status-pending { background: #ffc107; color: #000; }
|
|
.status-sent { background: #28a745; color: #fff; }
|
|
.status-failed { background: #dc3545; color: #fff; }
|
|
.status-cancelled { background: #6c757d; color: #fff; }
|
|
|
|
.message-preview {
|
|
background: #f8f9fa;
|
|
padding: 10px;
|
|
border-radius: 8px;
|
|
margin-top: 10px;
|
|
font-size: 0.9rem;
|
|
max-height: 100px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.filter-section {
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="text-white">
|
|
<i class="fas fa-calendar-alt"></i> Mensajes Programados
|
|
</h1>
|
|
<a href="index.php" class="btn btn-light">
|
|
<i class="fas fa-arrow-left"></i> Volver
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Filtros -->
|
|
<div class="filter-section">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Cliente</label>
|
|
<select class="form-select" id="filter-user">
|
|
<option value="">Todos los clientes</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Estado</label>
|
|
<select class="form-select" id="filter-status">
|
|
<option value="">Todos</option>
|
|
<option value="pending" selected>Pendientes</option>
|
|
<option value="sent">Enviados</option>
|
|
<option value="failed">Fallidos</option>
|
|
<option value="cancelled">Cancelados</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Desde</label>
|
|
<input type="date" class="form-control" id="filter-date-from">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Hasta</label>
|
|
<input type="date" class="form-control" id="filter-date-to">
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label"> </label>
|
|
<button class="btn btn-primary w-100" onclick="loadScheduledMessages()">
|
|
<i class="fas fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Lista de mensajes -->
|
|
<div class="card">
|
|
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-list"></i> Mensajes Programados
|
|
</h5>
|
|
<span class="badge bg-light text-dark" id="total-count">0 mensajes</span>
|
|
</div>
|
|
<div class="card-body" id="messages-container">
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-spinner fa-spin fa-3x text-muted"></i>
|
|
<p class="mt-3 text-muted">Cargando mensajes...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal para editar mensaje -->
|
|
<div class="modal fade" id="editModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-primary text-white">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-edit"></i> Editar Mensaje Programado
|
|
</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="edit-message-id">
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Fecha</label>
|
|
<input type="date" class="form-control" id="edit-date">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Hora</label>
|
|
<input type="time" class="form-control" id="edit-time">
|
|
</div>
|
|
|
|
<div id="edit-parameters-container"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
|
Cancelar
|
|
</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveEdit()">
|
|
<i class="fas fa-save"></i> Guardar Cambios
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
let currentMessages = [];
|
|
|
|
// Cargar usuarios para el filtro
|
|
async function loadUsers() {
|
|
try {
|
|
const response = await fetch('api/get_users.php');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const select = document.getElementById('filter-user');
|
|
data.data.forEach(user => {
|
|
const option = document.createElement('option');
|
|
option.value = user.id;
|
|
option.textContent = `${user.name} (${user.phone_number})`;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading users:', error);
|
|
}
|
|
}
|
|
|
|
// Cargar mensajes programados
|
|
async function loadScheduledMessages() {
|
|
const container = document.getElementById('messages-container');
|
|
container.innerHTML = '<div class="text-center py-5"><i class="fas fa-spinner fa-spin fa-3x text-muted"></i></div>';
|
|
|
|
try {
|
|
const userId = document.getElementById('filter-user').value;
|
|
const status = document.getElementById('filter-status').value;
|
|
const dateFrom = document.getElementById('filter-date-from').value;
|
|
const dateTo = document.getElementById('filter-date-to').value;
|
|
|
|
let url = 'api/get_scheduled_messages.php?limit=100';
|
|
if (userId) url += `&user_id=${userId}`;
|
|
if (status) url += `&status=${status}`;
|
|
if (dateFrom) url += `&date_from=${dateFrom}`;
|
|
if (dateTo) url += `&date_to=${dateTo}`;
|
|
|
|
console.log('Fetching:', url);
|
|
const response = await fetch(url);
|
|
console.log('Response status:', response.status);
|
|
const data = await response.json();
|
|
console.log('Response data:', data);
|
|
|
|
if (data.success) {
|
|
currentMessages = data.data;
|
|
renderMessages(data.data);
|
|
document.getElementById('total-count').textContent = `${data.total} mensaje${data.total !== 1 ? 's' : ''}`;
|
|
} else {
|
|
container.innerHTML = `<div class="alert alert-danger">Error: ${data.error || 'Error desconocido'}</div>`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error completo:', error);
|
|
container.innerHTML = `<div class="alert alert-danger">Error de conexión: ${error.message}</div>`;
|
|
}
|
|
}
|
|
|
|
// Renderizar mensajes
|
|
function renderMessages(messages) {
|
|
const container = document.getElementById('messages-container');
|
|
|
|
if (messages.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-calendar-times fa-3x text-muted"></i>
|
|
<p class="mt-3 text-muted">No hay mensajes programados</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
let html = '<div class="row">';
|
|
|
|
messages.forEach(msg => {
|
|
const statusClass = `status-${msg.status}`;
|
|
const date = new Date(msg.scheduled_date + ' ' + msg.scheduled_time);
|
|
const dateStr = date.toLocaleDateString('es-ES', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
const timeStr = date.toLocaleTimeString('es-ES', {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
|
|
let preview = '';
|
|
if (msg.message_type === 'template') {
|
|
preview = `<strong>Plantilla:</strong> ${msg.template_display_name || msg.template_name}`;
|
|
if (msg.template_body) {
|
|
let bodyPreview = msg.template_body;
|
|
|
|
if (msg.template_parameters) {
|
|
const isArray = Array.isArray(msg.template_parameters);
|
|
const isObject = typeof msg.template_parameters === 'object' && !isArray;
|
|
|
|
if (isArray) {
|
|
// Array indexado: [0] => "valor1", [1] => "valor2"
|
|
const regex = /\{\{[^\}]+\}\}/g;
|
|
let paramIndex = 0;
|
|
|
|
bodyPreview = bodyPreview.replace(regex, (match) => {
|
|
const value = msg.template_parameters[paramIndex] || match;
|
|
paramIndex++;
|
|
return `<strong>${value}</strong>`;
|
|
});
|
|
} else if (isObject) {
|
|
// Objeto asociativo: {nombre_tema: "valor1", fecha: "valor2"}
|
|
const regex = /\{\{([^\}]+)\}\}/g;
|
|
|
|
bodyPreview = bodyPreview.replace(regex, (match, varName) => {
|
|
const value = msg.template_parameters[varName] || match;
|
|
return `<strong>${value}</strong>`;
|
|
});
|
|
}
|
|
}
|
|
|
|
preview += `<div class="message-preview">${bodyPreview}</div>`;
|
|
}
|
|
} else {
|
|
preview = `<div class="message-preview">${msg.message_content}</div>`;
|
|
}
|
|
|
|
html += `
|
|
<div class="col-md-6">
|
|
<div class="card scheduled-message-card">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-user"></i> ${msg.user_name}
|
|
<small class="text-muted">(${msg.phone_number})</small>
|
|
</h6>
|
|
<span class="badge ${statusClass}">${getStatusText(msg.status)}</span>
|
|
</div>
|
|
|
|
<div class="mb-2">
|
|
<i class="fas fa-calendar"></i> ${dateStr}
|
|
<br>
|
|
<i class="fas fa-clock"></i> ${timeStr}
|
|
</div>
|
|
|
|
${preview}
|
|
|
|
${msg.error_message ? `<div class="alert alert-danger mt-2 mb-0"><small>${msg.error_message}</small></div>` : ''}
|
|
|
|
<div class="mt-3 d-flex gap-2">
|
|
${msg.status === 'pending' ? `
|
|
<button class="btn btn-sm btn-primary" onclick="editMessage(${msg.id})">
|
|
<i class="fas fa-edit"></i> Editar
|
|
</button>
|
|
<button class="btn btn-sm btn-warning" onclick="cancelMessage(${msg.id})">
|
|
<i class="fas fa-ban"></i> Cancelar
|
|
</button>
|
|
` : ''}
|
|
<button class="btn btn-sm btn-danger" onclick="deleteMessage(${msg.id})">
|
|
<i class="fas fa-trash"></i> Eliminar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
html += '</div>';
|
|
container.innerHTML = html;
|
|
}
|
|
|
|
function getStatusText(status) {
|
|
const texts = {
|
|
'pending': 'Pendiente',
|
|
'sent': 'Enviado',
|
|
'failed': 'Fallido',
|
|
'cancelled': 'Cancelado'
|
|
};
|
|
return texts[status] || status;
|
|
}
|
|
|
|
// Editar mensaje
|
|
function editMessage(id) {
|
|
const msg = currentMessages.find(m => m.id == id);
|
|
if (!msg) return;
|
|
|
|
document.getElementById('edit-message-id').value = msg.id;
|
|
document.getElementById('edit-date').value = msg.scheduled_date;
|
|
document.getElementById('edit-time').value = msg.scheduled_time;
|
|
|
|
// Si tiene parámetros de plantilla, mostrar campos para editarlos
|
|
const paramsContainer = document.getElementById('edit-parameters-container');
|
|
paramsContainer.innerHTML = '';
|
|
|
|
if (msg.template_parameters && Array.isArray(msg.template_parameters)) {
|
|
paramsContainer.innerHTML = '<label class="form-label">Parámetros de la Plantilla</label>';
|
|
msg.template_parameters.forEach((param, index) => {
|
|
paramsContainer.innerHTML += `
|
|
<div class="mb-2">
|
|
<input type="text"
|
|
class="form-control form-control-sm"
|
|
id="edit-param-${index}"
|
|
value="${param}"
|
|
placeholder="Variable ${index + 1}">
|
|
</div>
|
|
`;
|
|
});
|
|
}
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('editModal'));
|
|
modal.show();
|
|
}
|
|
|
|
// Guardar cambios
|
|
async function saveEdit() {
|
|
const id = document.getElementById('edit-message-id').value;
|
|
const date = document.getElementById('edit-date').value;
|
|
const time = document.getElementById('edit-time').value;
|
|
|
|
// Recopilar parámetros editados
|
|
const msg = currentMessages.find(m => m.id == id);
|
|
let parameters = null;
|
|
|
|
if (msg && msg.template_parameters && Array.isArray(msg.template_parameters)) {
|
|
parameters = [];
|
|
msg.template_parameters.forEach((_, index) => {
|
|
const input = document.getElementById(`edit-param-${index}`);
|
|
if (input) {
|
|
parameters.push(input.value);
|
|
}
|
|
});
|
|
}
|
|
|
|
try {
|
|
const payload = {
|
|
id: parseInt(id),
|
|
scheduled_date: date,
|
|
scheduled_time: time
|
|
};
|
|
|
|
if (parameters) {
|
|
payload.template_parameters = parameters;
|
|
}
|
|
|
|
const response = await fetch('api/update_scheduled_message.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
alert('Mensaje actualizado exitosamente');
|
|
bootstrap.Modal.getInstance(document.getElementById('editModal')).hide();
|
|
loadScheduledMessages();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('Error al guardar cambios');
|
|
}
|
|
}
|
|
|
|
// Cancelar mensaje
|
|
async function cancelMessage(id) {
|
|
if (!confirm('¿Cancelar este mensaje programado?')) return;
|
|
|
|
try {
|
|
const response = await fetch('api/update_scheduled_message.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id, status: 'cancelled' })
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
loadScheduledMessages();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('Error al cancelar mensaje');
|
|
}
|
|
}
|
|
|
|
// Eliminar mensaje
|
|
async function deleteMessage(id) {
|
|
if (!confirm('¿Eliminar este mensaje programado? Esta acción no se puede deshacer.')) return;
|
|
|
|
try {
|
|
const response = await fetch('api/delete_scheduled_message.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id })
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
loadScheduledMessages();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('Error al eliminar mensaje');
|
|
}
|
|
}
|
|
|
|
// Inicializar
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
loadUsers();
|
|
loadScheduledMessages();
|
|
|
|
// Establecer fecha de hoy como mínimo
|
|
const today = new Date().toISOString().split('T')[0];
|
|
document.getElementById('filter-date-from').value = today;
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|