Primer commit del sistema separado falta mejorar mucho

This commit is contained in:
nickpons666
2025-12-30 01:18:46 -06:00
commit 1679c73e52
2384 changed files with 472342 additions and 0 deletions

View File

@@ -0,0 +1,635 @@
<?php
session_start();
// Habilitar logging para depuración
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . '/../../../shared/utils/helpers.php';
require_once __DIR__ . '/../../../shared/auth/jwt.php';
require_once __DIR__ . '/../../../shared/database/connection.php';
$userData = JWTAuth::requireAuth();
// Verificar permiso para enviar mensajes
if (!hasPermission('send_messages', 'telegram')) {
die('No tienes permiso para crear y enviar mensajes de Telegram.');
}
$db = getDB();
// Obtener plantillas para el selector
$stmt = $db->query("SELECT id, nombre, contenido FROM plantillas_telegram ORDER BY nombre ASC");
$plantillas = $stmt->fetchAll();
// Obtener destinatarios guardados (si existen)
$stmt = $db->query("SELECT id, nombre, telegram_id as identificador, tipo FROM destinatarios_telegram ORDER BY nombre ASC");
$destinatarios = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="<?php echo $userData->idioma ?? 'es'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crear Mensaje Telegram - Sistema de Bots</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<style>
:root {
--telegram-color: #0088cc;
--telegram-dark: #006699;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, var(--telegram-color) 0%, var(--telegram-dark) 100%);
min-height: 100vh;
padding: 20px;
}
.header {
background: white;
border-radius: 15px;
padding: 20px 30px;
margin-bottom: 30px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.header h1 {
color: var(--telegram-color);
font-size: 24px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.form-container {
background: white;
border-radius: 15px;
padding: 40px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 25px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 600;
font-size: 14px;
}
.form-control {
width: 100%;
padding: 12px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: var(--telegram-color);
}
.form-help {
font-size: 13px;
color: #666;
margin-top: 5px;
}
.btn {
padding: 12px 24px;
border-radius: 8px;
text-decoration: none;
transition: all 0.2s;
border: none;
cursor: pointer;
font-size: 15px;
font-weight: 600;
}
.btn-primary {
background: var(--telegram-color);
color: white;
}
.btn-primary:hover {
background: var(--telegram-dark);
transform: translateY(-2px);
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-success {
background: #28a745;
color: white;
}
.form-actions {
display: flex;
gap: 15px;
margin-top: 30px;
border-top: 1px solid #eee;
padding-top: 20px;
}
/* Select2 Customization */
.select2-container--default .select2-selection--multiple {
border: 2px solid #e0e0e0 !important;
border-radius: 8px !important;
min-height: 45px;
}
.select2-container--default.select2-container--focus .select2-selection--multiple {
border-color: var(--telegram-color) !important;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: var(--telegram-color);
color: white;
border: 1px solid var(--telegram-dark);
border-radius: 4px;
padding: 3px 8px;
margin-top: 7px;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: white;
margin-right: 5px;
}
/* Summernote Customization */
.note-editor {
border: 2px solid #e0e0e0;
border-radius: 8px;
}
.note-editor.note-frame {
border-color: var(--telegram-color);
}
/* Modal Galería */
.modal {
display: none;
position: fixed;
z-index: 9999;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
}
.modal-content {
background: white;
margin: 50px auto;
padding: 30px;
border-radius: 15px;
width: 90%;
max-width: 900px;
max-height: 80vh;
overflow-y: auto;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
margin-top: 20px;
}
.gallery-item {
cursor: pointer;
border: 3px solid transparent;
border-radius: 10px;
overflow: hidden;
transition: all 0.3s;
}
.gallery-item:hover {
border-color: var(--telegram-color);
transform: scale(1.05);
}
.gallery-item img {
width: 100%;
height: 150px;
object-fit: cover;
display: block;
}
.close {
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
/* Scheduled/Recurring styles */
.ml-3 { margin-left: 1rem; }
</style>
</head>
<body>
<div class="header">
<h1><i class="fas fa-paper-plane"></i> Crear Mensaje Telegram</h1>
<a href="/telegram/dashboard_telegram.php" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Volver
</a>
</div>
<div class="container">
<div class="form-container">
<div id="alert-messages"></div>
<form id="messageForm">
<div class="form-group">
<label for="destinatario_id">Destinatarios (Canal ID o Usuario ID) *</label>
<div style="display: flex; gap: 10px;">
<div style="flex-grow: 1;">
<select id="destinatario_select" class="form-control" style="width: 100%;" multiple="multiple">
<?php foreach ($destinatarios as $dest):
$selected = ''; // Lógica para preseleccionar si es necesario
// Ejemplo: if (in_array($dest['identificador'], $existing_destinatarios)) { $selected = 'selected'; }
echo "<option value=\"" . htmlspecialchars($dest['identificador']) . "\" $selected>";
echo htmlspecialchars($dest['nombre']) . " (". $dest['tipo'] . ")";
echo "</option>";
endforeach; ?>
</select>
</div>
<div style="flex-grow: 1;">
<input type="text" id="destinatario_manual" name="destinatario_manual" class="form-control" placeholder="O pega IDs manualmente aquí, separados por comas">
</div>
</div>
<div class="form-help">Selecciona uno o varios destinatarios guardados, o ingresa IDs manualmente separados por comas.</div>
</div>
<div class="form-group">
<label for="plantilla_id">Cargar Plantilla (Opcional)</label>
<select id="plantilla_id" class="form-control" onchange="loadTemplate(this.value)">
<option value="">-- Seleccionar Plantilla --</option>
<?php foreach ($plantillas as $plantilla):
echo "<option value=\"" . htmlspecialchars($plantilla['id']) . "\">";
echo htmlspecialchars($plantilla['nombre']);
echo "</option>";
endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="contenido">Contenido del Mensaje *</label>
<button type="button" onclick="openGallery()" class="btn btn-success" style="margin-bottom: 10px; padding: 5px 10px; font-size: 12px;">
<i class="fas fa-images"></i> Insertar Imagen
</button>
<textarea id="contenido" name="contenido"></textarea>
</div>
<div class="form-group">
<label>Tipo de Envío *</label>
<div>
<input type="radio" id="send_immediate" name="send_type" value="inmediato" checked>
<label for="send_immediate">Inmediato</label>
<input type="radio" id="send_scheduled" name="send_type" value="programado" class="ml-3">
<label for="send_scheduled">Programado</label>
<input type="radio" id="send_recurring" name="send_type" value="recurrente" class="ml-3">
<label for="send_recurring">Recurrente</label>
</div>
</div>
<div id="scheduled_options" class="form-group" style="display: none;">
<label for="schedule_datetime">Fecha y Hora de Envío *</label>
<input type="datetime-local" id="schedule_datetime" name="schedule_datetime" class="form-control">
</div>
<div id="recurring_options" class="form-group" style="display: none;">
<label for="recurrence_frequency">Frecuencia de Recurrencia *</label>
<select id="recurrence_frequency" name="recurrence_frequency" class="form-control">
<option value="diario">Diario</option>
<option value="semanal">Semanal</option>
<option value="mensual">Mensual</option>
</select>
<div id="recurring_details" style="margin-top: 15px;">
<!-- Detalles específicos de recurrencia (día de la semana, día del mes) -->
<div id="weekly_options" style="display: none;">
<label for="recurrence_day_of_week">Día de la Semana</label>
<select id="recurrence_day_of_week" name="recurrence_day_of_week" class="form-control">
<option value="1">Lunes</option>
<option value="2">Martes</option>
<option value="3">Miércoles</option>
<option value="4">Jueves</option>
<option value="5">Viernes</option>
<option value="6">Sábado</option>
<option value="7">Domingo</option>
</select>
</div>
<div id="monthly_options" style="display: none;">
<label for="recurrence_day_of_month">Día del Mes</label>
<input type="number" id="recurrence_day_of_month" name="recurrence_day_of_month" class="form-control" min="1" max="31">
</div>
<label for="recurrence_time">Hora de Envío</label>
<input type="time" id="recurrence_time" name="recurrence_time" class="form-control">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary" id="btnEnviar">
<i class="fas fa-paper-plane"></i> Enviar Ahora
</button>
<button type="button" class="btn btn-secondary" onclick="previewMessage()">
<i class="fas fa-eye"></i> Vista Previa
</button>
</div>
</form>
</div>
</div>
<!-- Modal Galería -->
<div id="galleryModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeGallery()">&times;</span>
<h2>Galería de Imágenes</h2>
<div class="gallery-grid" id="galleryGrid"></div>
</div>
</div>
<!-- Templates Data Hidden -->
<script>
const templates = <?php echo json_encode($plantillas); ?>;
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
$('#contenido').summernote({
height: 300,
toolbar: [
['style', ['style']],
['font', ['bold', 'underline', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['insert', ['link']],
['view', ['fullscreen', 'codeview', 'help']]
]
});
// Inicializar Select2 para selección múltiple
$('#destinatario_select').select2({
placeholder: "-- Seleccionar Destinatarios Guardados --",
allowClear: true // Permite deseleccionar
});
// Sincronizar select con input manual
$('#destinatario_select').on('change', function() {
const selectedIds = $(this).val() || []; // Array de IDs del select2
const manualIds = $('#destinatario_manual').val().split(',').map(id => id.trim()).filter(id => id !== '');
// Combinar y eliminar duplicados
const combinedIds = [...new Set([...selectedIds, ...manualIds])];
// Actualizar el campo manual para reflejar todas las selecciones y entradas manuales
$('#destinatario_manual').val(combinedIds.join(', '));
});
$('#destinatario_manual').on('input', function() {
const manualIds = $(this).val().split(',').map(id => id.trim()).filter(id => id !== '');
const selectedIds = $('#destinatario_select').val() || []; // IDs del select2
// Asegurarse de que el select2 no se deseleccione si se añade manualmente
// Esto es complejo si se quiere mantener el estado exacto en ambos sentidos.
// Para simplificar, solo aseguramos que el input manual tenga todos los IDs.
const combinedIds = [...new Set([...manualIds, ...selectedIds])];
// Intentar seleccionar en select2 lo que está en manual si existe
// No actualizamos directamente el select2.val() para evitar ciclos y complejidades.
// El input manual es el que "manda" en caso de conflicto directo.
});
// Lógica para mostrar/ocultar campos de programación/recurrencia
const sendTypeRadios = document.querySelectorAll('input[name="send_type"]');
const scheduledOptions = document.getElementById('scheduled_options');
const recurringOptions = document.getElementById('recurring_options');
const recurrenceFrequency = document.getElementById('recurrence_frequency');
const weeklyOptions = document.getElementById('weekly_options');
const monthlyOptions = document.getElementById('monthly_options');
function toggleSendTypeOptions() {
const selectedSendType = document.querySelector('input[name="send_type"]:checked').value;
scheduledOptions.style.display = 'none';
recurringOptions.style.display = 'none';
if (selectedSendType === 'programado') {
scheduledOptions.style.display = 'block';
} else if (selectedSendType === 'recurrente') {
recurringOptions.style.display = 'block';
toggleRecurringDetails(); // Mostrar detalles específicos al cargar
}
}
function toggleRecurringDetails() {
const selectedFrequency = recurrenceFrequency.value;
weeklyOptions.style.display = 'none';
monthlyOptions.style.display = 'none';
if (selectedFrequency === 'semanal') {
weeklyOptions.style.display = 'block';
} else if (selectedFrequency === 'mensual') {
monthlyOptions.style.display = 'block';
}
}
// Inicializar al cargar la página
toggleSendTypeOptions();
// Escuchar cambios en los tipos de envío
sendTypeRadios.forEach(radio => {
radio.addEventListener('change', toggleSendTypeOptions);
});
// Escuchar cambios en la frecuencia de recurrencia
recurrenceFrequency.addEventListener('change', toggleRecurringDetails);
});
function loadTemplate(id) {
if (!id) return;
const template = templates.find(t => t.id == id);
if (template) {
if (confirm('¿Reemplazar el contenido actual con la plantilla?')) {
$('#contenido').summernote('code', template.contenido);
}
}
}
function openGallery() {
$('#galleryModal').show();
loadGalleryImages();
}
function closeGallery() {
$('#galleryModal').hide();
}
async function loadGalleryImages() {
try {
const response = await fetch('/gallery/api/list.php');
const data = await response.json();
const grid = document.getElementById('galleryGrid');
if (data.success && data.images.length > 0) {
grid.innerHTML = data.images.map(img => `
<div class="gallery-item" onclick="insertImage('${img.url}')">
<img src="${img.url_thumbnail}" alt="${img.nombre_original}">
</div>
`).join('');
} else {
grid.innerHTML = '<p>No hay imágenes.</p>';
}
} catch (e) {
console.error(e);
}
}
function insertImage(url) {
const fullUrl = window.location.origin + url;
$('#contenido').summernote('insertImage', fullUrl);
closeGallery();
}
function previewMessage() {
const content = $('#contenido').summernote('code');
const win = window.open('', 'Preview', 'width=800,height=600');
win.document.write('<div style="padding:20px;font-family:sans-serif;">' + content + '</div>');
}
$('#messageForm').on('submit', async function(e) {
e.preventDefault();
const selectedDestinatarios = $('#destinatario_select').val() || []; // Array de IDs del select2
const manualDestinatarios = $('#destinatario_manual').val()
.split(',')
.map(id => id.trim())
.filter(id => id !== '');
// Combinar y eliminar duplicados de ambos orígenes
const destinatarios = [...new Set([...selectedDestinatarios, ...manualDestinatarios])];
const contenido = $('#contenido').summernote('code');
const sendType = document.querySelector('input[name="send_type"]:checked').value;
let scheduleData = {};
if (destinatarios.length === 0 || !contenido) {
alert('Por favor selecciona al menos un destinatario y escribe un mensaje.');
return;
}
if (sendType === 'programado') {
const scheduleDatetime = $('#schedule_datetime').val();
if (!scheduleDatetime) {
alert('Por favor, selecciona la fecha y hora de envío programado.');
return;
}
scheduleData = {
fecha_envio: scheduleDatetime
};
} else if (sendType === 'recurrente') {
const recurrenceFrequencyVal = $('#recurrence_frequency').val();
const recurrenceTime = $('#recurrence_time').val();
if (!recurrenceFrequencyVal || !recurrenceTime) {
alert('Por favor, completa la frecuencia y hora de envío recurrente.');
return;
}
scheduleData = {
frecuencia: recurrenceFrequencyVal,
hora_envio: recurrenceTime
};
if (recurrenceFrequencyVal === 'semanal') {
const dayOfWeek = $('#recurrence_day_of_week').val();
if (!dayOfWeek) {
alert('Por favor, selecciona el día de la semana para el envío recurrente.');
return;
}
scheduleData.dia_semana = dayOfWeek;
} else if (recurrenceFrequencyVal === 'mensual') {
const dayOfMonth = $('#recurrence_day_of_month').val();
if (!dayOfMonth || dayOfMonth < 1 || dayOfMonth > 31) {
alert('Por favor, ingresa un día válido del mes (1-31) para el envío recurrente.');
return;
}
scheduleData.dia_mes = dayOfMonth;
}
}
if (!confirm(`¿Enviar mensaje (${sendType}) a ${destinatarios.length} destinatario(s)?`)) return;
$('#btnEnviar').prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Enviando...');
try {
const response = await fetch('/telegram/api/messages/send.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
destinatario_id: destinatarios, // Ahora puede ser un array
contenido: contenido,
tipo_envio: sendType,
...scheduleData // Añadir datos de programación/recurrencia
})
});
const result = await response.json();
if (result.success) {
alert('¡Mensaje enviado correctamente!');
window.location.href = '/telegram/views/messages/sent.php';
} else {
alert('Error: ' + (result.error || 'Error desconocido'));
}
} catch (error) {
console.error(error);
alert('Error de conexión');
} finally {
$('#btnEnviar').prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Enviar Ahora');
}
});
// Cerrar modal click fuera
window.onclick = function(event) {
if (event.target == document.getElementById('galleryModal')) {
closeGallery();
}
}
function showAlert(message, type) {
const alertDiv = `<div class="alert alert-\"${type}\"">${message}</div>`;
$('#alert-messages').html(alertDiv);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,6 @@
[30-Nov-2025 15:24:34 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number in /var/www/html/bot/telegram/views/messages/sent.php:51
Stack trace:
#0 /var/www/html/bot/telegram/views/messages/sent.php(51): PDOStatement->execute()
#1 {main}
thrown in /var/www/html/bot/telegram/views/messages/sent.php on line 51
[30-Nov-2025 16:36:16 America/Mexico_City] PHP Deprecated: strtotime(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/html/bot/telegram/views/messages/sent.php on line 279

373
telegram/views/messages/sent.php Executable file
View File

@@ -0,0 +1,373 @@
<?php
session_start();
// Habilitar logging para depuración
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . '/../../../shared/utils/helpers.php';
require_once __DIR__ . '/../../../shared/auth/jwt.php';
require_once __DIR__ . '/../../../shared/database/connection.php';
$userData = JWTAuth::requireAuth();
$db = getDB();
// Paginación
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 10;
$offset = ($page - 1) * $limit;
// Obtener estado de filtro si existe
$filterStatus = $_GET['status'] ?? 'todos'; // Default a 'todos'
// Construir la cláusula WHERE para filtrar por estado
$whereClause = "WHERE m.estado != 'deshabilitado'"; // No mostrar deshabilitados por defecto en la lista principal
$params = [];
if ($filterStatus !== 'todos') {
$whereClause = "WHERE m.estado = ?";
$params[] = $filterStatus;
}
// Obtener total de mensajes (según filtro)
$stmt = $db->prepare("SELECT COUNT(*) FROM mensajes_telegram m {$whereClause}");
$stmt->execute($params);
$totalMessages = $stmt->fetchColumn();
$totalPages = ceil($totalMessages / $limit);
// Obtener mensajes (según filtro)
$stmt = $db->prepare("
SELECT m.*, u.username
FROM mensajes_telegram m
LEFT JOIN usuarios u ON m.usuario_id = u.id
{$whereClause}
ORDER BY m.fecha_envio DESC
LIMIT ? OFFSET ?
");
$stmt->bindValue(count($params) + 1, $limit, PDO::PARAM_INT);
$stmt->bindValue(count($params) + 2, $offset, PDO::PARAM_INT);
// Bind WHERE parameters manually
foreach ($params as $k => $v) {
$stmt->bindValue($k + 1, $v);
}
$stmt->execute();
$mensajes = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="<?php echo $userData->idioma ?? 'es'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mensajes - Telegram</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--telegram-color: #0088cc;
--telegram-dark: #006699;
--bg-color: #f0f2f5;
--text-color: #333;
--success: #28a745;
--warning: #ffc107;
--danger: #dc3545;
--info: #17a2b8;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-color);
min-height: 100vh;
padding: 20px;
}
.header {
background: white;
border-radius: 15px;
padding: 20px 30px;
margin-bottom: 30px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.header h1 { color: var(--telegram-color); font-size: 24px; }
.container { max-width: 1200px; margin: 0 auto; }
.messages-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.message-card {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s;
}
.message-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
.message-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-size: 14px;
color: #666;
}
.message-dest {
font-weight: 600;
color: var(--telegram-color);
background: #eef0ff;
padding: 2px 8px;
border-radius: 10px;
}
.message-status {
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
text-transform: capitalize;
color: white;
}
.status-enviado { background-color: var(--success); }
.status-pendiente { background-color: var(--warning); color: #333; }
.status-fallido { background-color: var(--danger); }
.status-deshabilitado { background-color: var(--secondary-color); }
.message-content {
background: #f8f9fa;
padding: 15px;
border-radius: 10px;
margin-bottom: 10px;
font-family: monospace;
white-space: pre-wrap;
max-height: 100px;
overflow-y: auto;
}
.message-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 13px;
color: #999;
}
.btn {
padding: 8px 16px;
border-radius: 8px;
text-decoration: none;
transition: all 0.2s;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 600;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-primary {
background: var(--telegram-color);
color: white;
}
.pagination {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 30px;
}
.page-link {
background: white;
color: var(--telegram-color);
padding: 8px 12px;
border-radius: 5px;
text-decoration: none;
font-weight: 600;
}
.page-link.active {
background: var(--telegram-color);
color: white;
}
.empty-state {
background: white;
border-radius: 15px;
padding: 40px;
text-align: center;
color: #666;
}
.filter-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
justify-content: center;
}
.filter-buttons .btn {
padding: 8px 15px;
font-size: 13px;
border: 1px solid #ddd;
color: #333;
background-color: #f8f9fa;
}
.filter-buttons .btn.active {
background-color: var(--telegram-color);
color: white;
border-color: var(--telegram-color);
}
</style>
</head>
<body>
<div class="header">
<h1><i class="fas fa-history"></i> Historial de Mensajes Telegram</h1>
<div style="display: flex; gap: 10px;">
<a href="/telegram/dashboard_telegram.php" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Volver
</a>
<a href="create.php" class="btn btn-primary">
<i class="fas fa-plus"></i> Nuevo Mensaje
</a>
</div>
</div>
<div class="container">
<div class="filter-buttons">
<a href="?status=todos" class="btn <?php echo $filterStatus === 'todos' ? 'active' : ''; ?>">Todos</a>
<a href="?status=enviado" class="btn <?php echo $filterStatus === 'enviado' ? 'active' : ''; ?>">Enviados</a>
<a href="?status=pendiente" class="btn <?php echo $filterStatus === 'pendiente' ? 'active' : ''; ?>">Pendientes</a>
<a href="?status=fallido" class="btn <?php echo $filterStatus === 'fallido' ? 'active' : ''; ?>">Fallidos</a>
<a href="?status=deshabilitado" class="btn <?php echo $filterStatus === 'deshabilitado' ? 'active' : ''; ?>">Deshabilitados</a>
</div>
<?php if (empty($mensajes)): ?>
<div class="empty-state">
<i class="fas fa-inbox" style="font-size: 48px; margin-bottom: 20px; color: #ddd;"></i>
<h2>No hay mensajes en este estado</h2>
<p>Los mensajes que cumplan este criterio aparecerán aquí.</p>
</div>
<?php else: ?>
<div class="messages-list">
<?php foreach ($mensajes as $msg): ?>
<div class="message-card">
<div class="message-header">
<div>
Enviado a: <span class="message-dest"><?php echo htmlspecialchars($msg['chat_id']); ?></span>
</div>
<div>
<span class="message-status status-<?php echo htmlspecialchars($msg['estado']); ?>">
<?php echo htmlspecialchars($msg['estado']); ?>
</span>
<i class="fas fa-clock" style="margin-left: 10px;"></i> <?php echo date('d/m/Y H:i', strtotime($msg['fecha_envio'] ?? 'now')); ?>
</div>
</div>
<div class="message-content"><?php echo strip_tags($msg['contenido']); ?></div>
<div class="message-footer">
<div>
<i class="fas fa-user"></i> Por: <?php echo htmlspecialchars($msg['username'] ?? 'Sistema'); ?>
</div>
<div>
ID Telegram: <?php echo htmlspecialchars($msg['mensaje_telegram_id'] ?? 'N/A'); ?>
<button class="btn btn-warning btn-sm" onclick="retryMessage(<?php echo $msg['id']; ?>)" style="margin-left: 15px;">
<i class="fas fa-sync-alt"></i> Reintentar
</button>
<button class="btn btn-danger btn-sm" onclick="deleteMessage(<?php echo $msg['id']; ?>)" style="margin-left: 5px;">
<i class="fas fa-trash"></i> Eliminar
</button>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($totalPages > 1): ?>
<div class="pagination">
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<a href="?page=<?php echo $i; ?>&status=<?php echo htmlspecialchars($filterStatus); ?>" class="page-link <?php echo $i === $page ? 'active' : ''; ?>">
<?php echo $i; ?>
</a>
<?php endfor; ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<script>
async function retryMessage(messageId) {
if (!confirm('¿Estás seguro de que quieres volver a poner este mensaje en la cola como "pendiente"?')) {
return;
}
try {
const response = await fetch('/telegram/api/messages/retry.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: messageId })
});
const result = await response.json();
if (result.success) {
alert('Mensaje marcado como pendiente.');
location.reload();
} else {
alert('Error al reintentar el mensaje: ' + (result.error || 'Error desconocido.'));
}
} catch (error) {
console.error('Error al enviar la solicitud de reintento:', error);
alert('Error de conexión al intentar reintentar el mensaje.');
}
}
async function deleteMessage(messageId) {
if (!confirm('¿Estás seguro de que quieres eliminar/deshabilitar este mensaje?')) {
return;
}
try {
const response = await fetch('/telegram/api/messages/delete.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: messageId })
});
const result = await response.json();
if (result.success) {
alert('Mensaje deshabilitado correctamente.');
location.reload(); // Recargar la página para actualizar la lista
} else {
alert('Error al deshabilitar el mensaje: ' + (result.error || 'Error desconocido.'));
}
} catch (error) {
console.error('Error al enviar la solicitud de eliminación:', error);
alert('Error de conexión al intentar deshabilitar el mensaje.');
}
}
</script>
</body>
</html>