Primer commit del sistema separado falta mejorar mucho
This commit is contained in:
736
discord/views/messages/create.php
Executable file
736
discord/views/messages/create.php
Executable file
@@ -0,0 +1,736 @@
|
||||
<?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', 'discord')) {
|
||||
die('No tienes permiso para crear y enviar mensajes de Discord.');
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// Obtener plantillas para el selector
|
||||
$stmt = $db->query("SELECT id, nombre, contenido FROM plantillas_discord ORDER BY nombre ASC");
|
||||
$plantillas = $stmt->fetchAll();
|
||||
|
||||
// Obtener destinatarios guardados (si existen)
|
||||
$stmt = $db->query("SELECT id, nombre, discord_id as identificador, tipo FROM destinatarios_discord 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 Discord - 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" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/4.1.5/css/flag-icons.min.css" />
|
||||
<style>
|
||||
/* Estilos para los botones de traducción */
|
||||
.translation-buttons {
|
||||
margin-bottom: 10px;
|
||||
padding: 5px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
background-color: #f8f9fa;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.translation-buttons .btn-translate {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
background: white;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.translation-buttons .btn-translate:hover {
|
||||
background-color: #e9ecef;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.translation-buttons .btn-translate:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.translation-buttons .flag-icon {
|
||||
width: 16px;
|
||||
height: 12px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 1px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.translation-buttons .loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:root {
|
||||
--discord-color: #5865F2;
|
||||
--discord-dark: #4752C4;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, var(--discord-color) 0%, var(--discord-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(--discord-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(--discord-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(--discord-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--discord-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--single {
|
||||
height: 45px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
line-height: 45px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||
height: 43px;
|
||||
}
|
||||
|
||||
/* 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(--discord-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;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1><i class="fas fa-paper-plane"></i> Crear Mensaje Discord</h1>
|
||||
<a href="/discord/dashboard_discord.php" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Volver
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<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): ?>
|
||||
<option value="<?php echo htmlspecialchars($dest['identificador']); ?>">
|
||||
<?php echo htmlspecialchars($dest['nombre']); ?> (<?php echo $dest['tipo']; ?>)
|
||||
</option>
|
||||
<?php 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): ?>
|
||||
<option value="<?php echo htmlspecialchars($plantilla['id']); ?>">
|
||||
<?php echo htmlspecialchars($plantilla['nombre']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="contenido">Contenido del Mensaje *</label>
|
||||
<div id="translation-buttons" class="translation-buttons" style="margin-bottom: 10px; display: flex; gap: 5px;">
|
||||
<!-- Los botones se cargarán dinámicamente con JavaScript -->
|
||||
</div>
|
||||
<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" class="form-control" rows="10" required></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()">×</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>
|
||||
// Función para cargar los idiomas activos
|
||||
function loadActiveLanguages() {
|
||||
return fetch('/shared/languages/get_active.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.languages) {
|
||||
return data.languages;
|
||||
}
|
||||
return [];
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error cargando idiomas:', error);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
// Función para crear botones de traducción
|
||||
function createTranslationButtons(languages) {
|
||||
const container = $('#translation-buttons');
|
||||
container.empty();
|
||||
|
||||
languages.forEach(lang => {
|
||||
if (lang.codigo !== 'es') { // No mostramos el botón para español (idioma original)
|
||||
const button = $(`
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary translate-btn"
|
||||
data-lang="${lang.codigo}"
|
||||
title="Traducir a ${lang.nombre}">
|
||||
<span class="flag-icon flag-icon-${lang.bandera || 'globe'}"></span>
|
||||
</button>
|
||||
`);
|
||||
container.append(button);
|
||||
}
|
||||
});
|
||||
|
||||
// Manejador de eventos para los botones de traducción
|
||||
$('.translate-btn').on('click', async function() {
|
||||
const targetLang = $(this).data('lang');
|
||||
const content = $('#contenido').val();
|
||||
|
||||
if (!content) {
|
||||
alert('Por favor, ingrese un mensaje para traducir.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/shared/translations/translate.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text: content,
|
||||
target: targetLang,
|
||||
source: 'es' // Asumimos que el texto original está en español
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Agregar la traducción al final del contenido
|
||||
const translatedText = `\n\n--- Traducción a ${targetLang.toUpperCase()} ---\n${data.translatedText}`;
|
||||
$('#contenido').val(content + translatedText);
|
||||
|
||||
// Eliminar el botón de traducción
|
||||
$(this).remove();
|
||||
} else {
|
||||
alert('Error al traducir: ' + (data.error || 'Error desconocido'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error al traducir:', error);
|
||||
alert('Error al conectar con el servicio de traducción.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cargar los botones de traducción al iniciar
|
||||
$(document).ready(function() {
|
||||
loadActiveLanguages().then(languages => {
|
||||
createTranslationButtons(languages);
|
||||
});
|
||||
$('#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
|
||||
$('#destinatario_select').val(manualIds).trigger('change');
|
||||
});
|
||||
});
|
||||
|
||||
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>');
|
||||
}
|
||||
|
||||
// Cerrar modal click fuera
|
||||
window.onclick = function(event) {
|
||||
if (event.target == document.getElementById('galleryModal')) {
|
||||
closeGallery();
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
$('#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('/discord/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 = '/discord/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');
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
39
discord/views/messages/php_errors.log
Executable file
39
discord/views/messages/php_errors.log
Executable file
@@ -0,0 +1,39 @@
|
||||
[29-Nov-2025 04:52:45 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'identificador' in 'field list' in /var/www/html/bot/discord/views/messages/create.php:21
|
||||
Stack trace:
|
||||
#0 /var/www/html/bot/discord/views/messages/create.php(21): PDO->query()
|
||||
#1 {main}
|
||||
thrown in /var/www/html/bot/discord/views/messages/create.php on line 21
|
||||
[29-Nov-2025 04:53:24 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'identificador' in 'field list' in /var/www/html/bot/discord/views/messages/create.php:21
|
||||
Stack trace:
|
||||
#0 /var/www/html/bot/discord/views/messages/create.php(21): PDO->query()
|
||||
#1 {main}
|
||||
thrown in /var/www/html/bot/discord/views/messages/create.php on line 21
|
||||
[29-Nov-2025 04:53:28 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'identificador' in 'field list' in /var/www/html/bot/discord/views/messages/create.php:21
|
||||
Stack trace:
|
||||
#0 /var/www/html/bot/discord/views/messages/create.php(21): PDO->query()
|
||||
#1 {main}
|
||||
thrown in /var/www/html/bot/discord/views/messages/create.php on line 21
|
||||
[29-Nov-2025 04:54:04 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'identificador' in 'field list' in /var/www/html/bot/discord/views/messages/create.php:21
|
||||
Stack trace:
|
||||
#0 /var/www/html/bot/discord/views/messages/create.php(21): PDO->query()
|
||||
#1 {main}
|
||||
thrown in /var/www/html/bot/discord/views/messages/create.php on line 21
|
||||
[30-Nov-2025 15:23:38 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number in /var/www/html/bot/discord/views/messages/sent.php:51
|
||||
Stack trace:
|
||||
#0 /var/www/html/bot/discord/views/messages/sent.php(51): PDOStatement->execute()
|
||||
#1 {main}
|
||||
thrown in /var/www/html/bot/discord/views/messages/sent.php on line 51
|
||||
[30-Nov-2025 15:24:23 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number in /var/www/html/bot/discord/views/messages/sent.php:51
|
||||
Stack trace:
|
||||
#0 /var/www/html/bot/discord/views/messages/sent.php(51): PDOStatement->execute()
|
||||
#1 {main}
|
||||
thrown in /var/www/html/bot/discord/views/messages/sent.php on line 51
|
||||
[30-Nov-2025 15:25:34 America/Mexico_City] PHP Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number in /var/www/html/bot/discord/views/messages/sent.php:51
|
||||
Stack trace:
|
||||
#0 /var/www/html/bot/discord/views/messages/sent.php(51): PDOStatement->execute()
|
||||
#1 {main}
|
||||
thrown in /var/www/html/bot/discord/views/messages/sent.php on line 51
|
||||
[30-Nov-2025 16:17:08 America/Mexico_City] PHP Deprecated: strtotime(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/html/bot/discord/views/messages/sent.php on line 283
|
||||
[30-Nov-2025 16:19:47 America/Mexico_City] PHP Deprecated: strtotime(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/html/bot/discord/views/messages/sent.php on line 283
|
||||
[30-Nov-2025 16:20:43 America/Mexico_City] PHP Deprecated: strtotime(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/html/bot/discord/views/messages/sent.php on line 283
|
||||
[30-Nov-2025 16:22:34 America/Mexico_City] PHP Deprecated: strtotime(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/html/bot/discord/views/messages/sent.php on line 283
|
||||
377
discord/views/messages/sent.php
Executable file
377
discord/views/messages/sent.php
Executable file
@@ -0,0 +1,377 @@
|
||||
<?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_discord 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_discord 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 - Discord</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--discord-color: #5865F2;
|
||||
--discord-dark: #4752C4;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, var(--discord-color) 0%, var(--discord-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(--discord-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(--discord-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: #28a745; }
|
||||
.status-pendiente { background-color: #ffc107; }
|
||||
.status-fallido { background-color: #dc3545; }
|
||||
.status-deshabilitado { background-color: #6c757d; }
|
||||
|
||||
.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(--discord-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.page-link {
|
||||
background: white;
|
||||
color: var(--discord-color);
|
||||
padding: 8px 12px;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-link.active {
|
||||
background: var(--discord-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(--discord-color);
|
||||
color: white;
|
||||
border-color: var(--discord-color);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1><i class="fas fa-history"></i> Historial de Mensajes Discord</h1>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<a href="/discord/dashboard_discord.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['canal_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 Discord: <?php echo htmlspecialchars($msg['mensaje_discord_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('/discord/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('/discord/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>
|
||||
Reference in New Issue
Block a user