Files
whatsapp/scheduled_messages.php
T
2026-02-03 21:30:55 -05:00

480 lines
20 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">&nbsp;</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 && Array.isArray(msg.template_parameters)) {
msg.template_parameters.forEach((param, index) => {
bodyPreview = bodyPreview.replace(`{{${index + 1}}}`, param);
});
}
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>