From 48671dc88e41bcdf160948592dc5f3979ba08f29 Mon Sep 17 00:00:00 2001 From: nickpons666 Date: Sat, 17 Jan 2026 16:13:19 -0600 Subject: [PATCH] Implementar tema claro/oscuro con Bootstrap 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar atributo data-bs-theme al HTML - Implementar botón toggle con íconos sol/luna en navegación - Agregar estilos CSS para modo oscuro (variables, componentes, tablas) - Implementar JavaScript para funcionalidad toggle con persistencia localStorage - Agregar detección automática del tema del sistema - Fix específico para columna "Contenido (Previo)" en sent_messages.php - Mejorar Content Security Policy para archivos .map de Bootstrap - Configuración de entorno automática para .env.pruebas Características: - Toggle claro/oscuro con persistencia - Detección automática de preferencias del sistema - Estilos personalizados para componentes en modo oscuro - Compatibilidad con todas las páginas del sistema --- assets/css/style.css | 422 ++++++++++++++++++++++++++++++++++ assets/js/main.js | 46 ++++ common/helpers/url_helper.php | 75 ++++++ config/config.php | 148 ++++++++++++ sent_messages.php | 171 ++++++++++++++ templates/header.php | 151 ++++++++++++ 6 files changed, 1013 insertions(+) create mode 100755 assets/css/style.css create mode 100755 assets/js/main.js create mode 100755 common/helpers/url_helper.php create mode 100755 config/config.php create mode 100755 sent_messages.php create mode 100755 templates/header.php diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100755 index 0000000..004eeca --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,422 @@ +/* Theme Toggle Styles */ +.theme-toggle-btn { + color: #f8f9fa !important; /* Light color for the icon */ + border: 1px solid #f8f9fa !important; /* Light color for the border */ + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; +} + +.theme-toggle-btn:hover { + background-color: #f8f9fa; + color: #212529 !important; /* Dark color for icon on hover */ +} + +/* General Styles */ +body { + overflow-x: hidden; +} + +/* Dark Mode Variables */ +[data-bs-theme="dark"] { + --bs-body-bg: #1a1a1a; + --bs-body-color: #e9ecef; + --bs-border-color: #495057; +} + + + +[data-bs-theme="dark"] #theme-toggle:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +/* Custom dark mode adjustments */ +[data-bs-theme="dark"] .message.in { + background-color: #2d3748; + color: #e2e8f0; +} + +[data-bs-theme="dark"] .message.out { + background-color: #2563eb; + color: white; +} + +[data-bs-theme="dark"] .card { + background-color: #2d3748; + border-color: #4a5568; +} + +[data-bs-theme="dark"] .table { + color: #e9ecef; +} + +[data-bs-theme="dark"] .form-control { + background-color: #2d3748; + border-color: #4a5568; + color: #e9ecef; +} + +[data-bs-theme="dark"] .form-control:focus { + background-color: #2d3748; + border-color: #2563eb; + color: #e9ecef; +} + +[data-bs-theme="dark"] .navbar { + background-color: #1a1a1a !important; +} + +[data-bs-theme="dark"] .dropdown-menu { + background-color: #2d3748; + border-color: #4a5568; +} + +[data-bs-theme="dark"] .dropdown-item { + color: #e9ecef; +} + +[data-bs-theme="dark"] .dropdown-item:hover { + background-color: #4a5568; + color: #e9ecef; +} + +/* Fix for message preview in dark mode */ +[data-bs-theme="dark"] .message-preview { + color: #e9ecef !important; + background-color: transparent !important; +} + +/* More specific fix for table cells in dark mode */ +[data-bs-theme="dark"] .table td { + color: #e9ecef !important; + border-color: #4a5568 !important; +} + +[data-bs-theme="dark"] .table tbody tr { + background-color: #2d3748 !important; +} + +[data-bs-theme="dark"] .table tbody tr:nth-child(odd) { + background-color: #1a202c !important; +} + +/* Universal fix for all text in dark mode tables */ +[data-bs-theme="dark"] .table * { + color: #e9ecef !important; +} + +[data-bs-theme="dark"] .table .text-muted { + color: #a0aec0 !important; +} + +[data-bs-theme="dark"] .table .badge { + color: white !important; +} + +/* Fix for card container in dark mode */ +[data-bs-theme="dark"] .card-body { + background-color: #2d3748 !important; + color: #e9ecef !important; +} + +/* Table container fix */ +[data-bs-theme="dark"] .table-responsive { + background-color: #2d3748 !important; +} + +/* Specific fix for text content in dark mode */ +[data-bs-theme="dark"] .text-content { + color: #212529 !important; + background-color: #f8f9fa !important; + padding: 8px !important; + border-radius: 4px !important; + display: inline-block !important; +} + +[data-bs-theme="dark"] td.text-break { + color: #212529 !important; + background-color: #f8f9fa !important; +} + +/* Alternative: Force dark theme for all table cells */ +[data-bs-theme="dark"] .table td:nth-child(3) { + color: #212529 !important; + background-color: #f8f9fa !important; +} + +[data-bs-theme="dark"] .table td:nth-child(3) div { + color: #212529 !important; + background-color: #f8f9fa !important; +} + +/* FIX FOR MESSAGE PREVIEW - MOST SPECIFIC SELECTORS */ +html[data-bs-theme="dark"] body .container-fluid .card .card-body .table-responsive .table tbody tr td:nth-child(3), +html[data-bs-theme="dark"] .table td:nth-child(3), +body[data-bs-theme="dark"] .table td:nth-child(3) { + background-color: #2d3748 !important; + border-color: #4a5568 !important; + color: #e9ecef !important; +} + +html[data-bs-theme="dark"] body .container-fluid .card .card-body .table-responsive .table tbody tr td:nth-child(3) .message-preview, +html[data-bs-theme="dark"] .table td:nth-child(3) .message-preview { + background-color: #374151 !important; + color: #f3f4f6 !important; + padding: 8px !important; + border-radius: 4px !important; + display: inline-block !important; + border: 1px solid #4a5568 !important; +} + +/* Override any conflicting styles */ +html[data-bs-theme="dark"] td:nth-child(3) *, +html[data-bs-theme="dark"] td:nth-child(3) div, +html[data-bs-theme="dark"] td:nth-child(3) span { + color: #f3f4f6 !important; +} + +/* ULTRA SPECIFIC - Target the exact table */ +#sent-messages-table[data-bs-theme="dark"] .table td:nth-child(3), +#sent-messages-table .table td:nth-child(3)[data-bs-theme="dark"], +.card[data-bs-theme="dark"] .table td:nth-child(3) { + background: #2d3748 !important; + color: #f3f4f6 !important; +} + +#sent-messages-table[data-bs-theme="dark"] .table td:nth-child(3) .message-preview, +#sent-messages-table .table td:nth-child(3)[data-bs-theme="dark"] .message-preview { + background: #374151 !important; + color: #ffffff !important; + padding: 8px !important; + border-radius: 4px !important; + border: 1px solid #4a5568 !important; +} + +/* Additional dark mode fixes */ +[data-bs-theme="dark"] .table-light { + background-color: #2d3748 !important; + color: #e9ecef !important; +} + +[data-bs-theme="dark"] .table-hover tbody tr:hover { + background-color: #4a5568 !important; +} + +[data-bs-theme="dark"] .text-muted { + color: #a0aec0 !important; +} + +[data-bs-theme="dark"] .badge { + color: white !important; +} + +[data-bs-theme="dark"] .badge.bg-info { + background-color: #3182ce !important; +} + +/* Estilos para el chat */ +#chat-history { + height: 70vh; + overflow-y: auto; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.message { + max-width: 80%; + padding: 0.75rem 1rem; + border-radius: 1rem; + word-wrap: break-word; + position: relative; + line-height: 1.4; +} + +/* Mensajes entrantes */ +.message.in { + background-color: #f0f0f0; + color: #333; + align-self: flex-start; + border-bottom-left-radius: 0.25rem; +} + +/* Mensajes salientes */ +.message.out { + background-color: #007bff; + color: white; + align-self: flex-end; + border-bottom-right-radius: 0.25rem; +} + +/* Estilo para el nombre del remitente en grupos */ +.message-sender { + font-weight: bold; + font-size: 0.85rem; + margin-bottom: 0.25rem; +} + +/* Estilo para el texto del mensaje */ +.message-text { + word-wrap: break-word; +} + +/* Estilo para el contenedor del formulario de mensajes */ +#message-form-container { + padding: 1rem; + border-top: 1px solid #dee2e6; + background-color: #f8f9fa; +} + +/* Estilos para la lista de usuarios */ +.user-list { + max-height: 80vh; + overflow-y: auto; +} + +.user-list .list-group-item { + cursor: pointer; + transition: background-color 0.2s; +} + +.user-list .list-group-item:hover { + background-color: #f8f9fa; +} + +.user-list .list-group-item.active { + background-color: #007bff; + border-color: #007bff; +} + +#wrapper { + display: flex; + transition: all 0.3s ease; +} + +#sidebar-wrapper { + display: none; +} + +#page-content-wrapper { + min-width: 100vw; + flex-grow: 1; +} + +/* Navbar styles */ +.navbar-brand { + font-weight: bold; + font-size: 1.2rem; +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.8); + transition: color 0.2s; +} + +.navbar-dark .navbar-nav .nav-link:hover { + color: white; +} + +.navbar-dark .navbar-nav .nav-link.active { + color: white; +} + +.dropdown-menu { + animation: slideDown 0.2s ease; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.dropdown-item { + transition: background-color 0.2s; +} + +.dropdown-item:hover {.theme-toggle-btn + background-color: #f8f9fa; +} + +.dropdown-item.active, .dropdown-item:active { + background-color: #0d6efd; + color: white; +} + +/* Responsive navbar */ +@media (max-width: 991px) { + .navbar-collapse { + margin-top: 1rem; + } + + .d-flex.align-items-center.gap-2 { + flex-direction: column; + align-items: flex-start !important; + margin-top: 1rem; + } +} + +#wrapper.toggled #page-content-wrapper { + min-width: calc(100vw - 250px); +} + +.sidebar-heading { + padding: 0.875rem 1.25rem; + font-size: 1.2rem; +} + +@media (min-width: 768px) { + #sidebar-wrapper { + margin-left: 0; + position: relative; /* Changed for desktop */ + z-index: 1; /* Changed for desktop */ + } + + #page-content-wrapper { + min-width: 0; + width: 100%; + } + + #wrapper.toggled #sidebar-wrapper { + margin-left: -250px; + } +} + +/* Overlay for mobile when sidebar is open */ +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 999; + display: none; + opacity: 0; /* Initial opacity */ + transition: opacity 0.3s ease-in-out; /* Smooth transition */ +} + +.overlay.show { + display: block; + opacity: 1; /* Full opacity when shown */ +} + +/* Estilos para el logo en el sidebar */ +#sidebar-logo { + width: 40px; /* Tamaño fijo para el ancho */ + height: auto; /* Mantiene la proporción de aspecto */ + vertical-align: middle; /* Alinea verticalmente con el texto */ + margin-left: 8px; /* Espaciado a la izquierda del logo */ +} + +.sidebar-heading { + display: flex; + align-items: center; /* Centra verticalmente el texto y el logo */ + padding: 0.875rem 1.25rem; + font-size: 1.2rem; + white-space: nowrap; /* Evita que el texto se rompa en varias líneas */ +} \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100755 index 0000000..f8d4f16 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,46 @@ +$(document).ready(function(){ + $("#menu-toggle").click(function(e) { + e.preventDefault(); + $("#wrapper").toggleClass("toggled"); + }); + + $(".overlay").click(function() { + $("#wrapper").removeClass("toggled"); + }); + + // Theme Toggle Functionality + const themeToggle = $('#theme-toggle'); + const themeIcon = $('#theme-icon'); + const html = $('html'); + + // Load saved theme or default to light + const savedTheme = localStorage.getItem('theme') || 'light'; + setTheme(savedTheme); + + themeToggle.on('click', function() { + const currentTheme = html.attr('data-bs-theme') || 'light'; + const newTheme = currentTheme === 'light' ? 'dark' : 'light'; + setTheme(newTheme); + localStorage.setItem('theme', newTheme); + }); + + function setTheme(theme) { + html.attr('data-bs-theme', theme); + + // Update icon + if (theme === 'dark') { + themeIcon.removeClass('bi-sun-fill').addClass('bi-moon-fill'); + themeToggle.attr('title', 'Cambiar a tema claro'); + } else { + themeIcon.removeClass('bi-moon-fill').addClass('bi-sun-fill'); + themeToggle.attr('title', 'Cambiar a tema oscuro'); + } + } + + // Check system preference on first load + if (!localStorage.getItem('theme')) { + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + setTheme(prefersDark ? 'dark' : 'light'); + localStorage.setItem('theme', prefersDark ? 'dark' : 'light'); + } +}); diff --git a/common/helpers/url_helper.php b/common/helpers/url_helper.php new file mode 100755 index 0000000..e050ae7 --- /dev/null +++ b/common/helpers/url_helper.php @@ -0,0 +1,75 @@ +load(); + } catch (Exception $e) { + die('Error al cargar el archivo de entorno en Docker: ' . $e->getMessage()); + } + } +} else { + // Entorno local: cargar archivo .env según APP_ENVIRONMENT + $env = getenv('APP_ENVIRONMENT') ?: ($_SERVER['APP_ENVIRONMENT'] ?? 'pruebas'); + $envFile = '.env'; + if ($env) { + $envFile = '.env.' . $env; + } + + $dotenv = null; + if (file_exists(dirname(__DIR__) . '/' . $envFile)) { + $dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__), $envFile); + } elseif (file_exists(dirname(__DIR__) . '/.env')) { + $dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__)); + } + + if ($dotenv) { + try { + $dotenv->load(); + } catch (Exception $e) { + die('Error al cargar el archivo de entorno: ' . $e->getMessage()); + } + $dotenv->required([ + 'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS', + 'JWT_SECRET', 'APP_URL' + ]); + } +} + +// Environment Configuration +define('ENVIRONMENT', $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'production'); + +// Detectar si se ejecuta desde la línea de comandos +$is_cli = (php_sapi_name() === 'cli' || defined('STDIN')); + +// Helper function to get env vars +function getEnvVar($name, $default = null) { + return $_ENV[$name] ?? $_SERVER[$name] ?? getenv($name) ?? $default; +} + +// Configurar la URL base y el protocolo +if ($is_cli) { + define('BOT_BASE_URL', getEnvVar('APP_URL')); + $protocol = 'http'; +} else { + $protocol = 'http'; + if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || + (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') || + (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] === 'on') || + (!empty($_SERVER['HTTP_CF_VISITOR']) && strpos($_SERVER['HTTP_CF_VISITOR'], 'https') !== false)) { + $protocol = 'https'; + $_SERVER['HTTPS'] = 'on'; + $_SERVER['SERVER_PORT'] = 443; + } + $app_url = getEnvVar('APP_URL'); + if ($app_url) { + define('BOT_BASE_URL', $app_url); + } else { + define('BOT_BASE_URL', $protocol . '://' . $_SERVER['HTTP_HOST']); + } + $_SERVER['REQUEST_SCHEME'] = $protocol; +} +define('BASE_PATH', dirname(__DIR__)); + +// Database Configuration +define('DB_HOST', getEnvVar('DB_HOST')); +define('DB_USER', getEnvVar('DB_USER')); +define('DB_PASS', getEnvVar('DB_PASS')); +define('DB_NAME', getEnvVar('DB_NAME')); +define('DB_DIALECT', getEnvVar('DB_DIALECT')); +define('DB_PORT', getEnvVar('DB_PORT')); + +// Session Configuration +define('SESSION_SECRET', getEnvVar('JWT_SECRET')); + +// Discord API Configuration +define('DISCORD_GUILD_ID', getEnvVar('DISCORD_GUILD_ID')); +define('DISCORD_CLIENT_ID', getEnvVar('DISCORD_CLIENT_ID')); +define('DISCORD_CLIENT_SECRET', getEnvVar('DISCORD_CLIENT_SECRET')); +define('DISCORD_BOT_TOKEN', getEnvVar('DISCORD_BOT_TOKEN')); + +// Telegram API Configuration +define('TELEGRAM_BOT_TOKEN', getEnvVar('TELEGRAM_BOT_TOKEN')); +define('TELEGRAM_WEBHOOK_TOKEN', getEnvVar('TELEGRAM_WEBHOOK_TOKEN')); + +// LibreTranslate Configuration +define('LIBRETRANSLATE_URL', getEnvVar('LIBRETRANSLATE_URL')); + +// N8N Configuration +define('N8N_URL', getEnvVar('N8N_URL')); +define('N8N_TOKEN', getEnvVar('N8N_TOKEN')); +define('N8N_PROCESS_QUEUE_WEBHOOK_URL', getEnvVar('N8N_PROCESS_QUEUE_WEBHOOK_URL')); +define('N8N_IA_WEBHOOK_URL', getEnvVar('N8N_IA_WEBHOOK_URL')); +define('N8N_IA_WEBHOOK_URL_DISCORD', getEnvVar('N8N_IA_WEBHOOK_URL_DISCORD')); +define('INTERNAL_API_KEY', getEnvVar('INTERNAL_API_KEY')); + +// Error Reporting +switch (ENVIRONMENT) { + case 'development': + error_reporting(E_ALL); + ini_set('display_errors', '1'); + ini_set('log_errors', '1'); + ini_set('error_log', dirname(__DIR__) . '/logs/php_errors.log'); + break; + case 'production': + error_reporting(E_ALL & ~E_DEPRECATED); + ini_set('display_errors', '0'); + ini_set('log_errors', '1'); + ini_set('error_log', dirname(__DIR__) . '/logs/php_errors.log'); + break; + default: + error_reporting(E_ALL); + ini_set('display_errors', '1'); + ini_set('log_errors', '1'); + ini_set('error_log', dirname(__DIR__) . '/logs/php_errors.log'); + break; +} + +// Helper function to get full URL +function url($path = '') { + $path = ltrim($path, '/'); + return BOT_BASE_URL . '/' . $path; +} + +// Helper function to get full asset URL +function asset_url($path = '') { + $path = ltrim($path, '/'); + return BOT_BASE_URL . '/assets/' . $path; +} diff --git a/sent_messages.php b/sent_messages.php new file mode 100755 index 0000000..4e3f778 --- /dev/null +++ b/sent_messages.php @@ -0,0 +1,171 @@ +prepare($query); + $stmt->execute(); + $sentMessages = $stmt->fetchAll(); + +} catch (PDOException $e) { + error_log("Error al consultar mensajes enviados: " . $e->getMessage()); + $sentMessages = []; + $db_error = "Error de base de datos: " . $e->getMessage(); +} + +$pageTitle = 'Mensajes Enviados'; +require_once __DIR__ . '/templates/header.php'; +?> + +
+

Mensajes Enviados

+ + +
+ + + + + +
Mensaje eliminado de con éxito.
+ +
+ Ocurrió un error desconocido.'; + if ($_GET['error'] === 'delete_failed') { + $errorMessage = "No se pudo eliminar el mensaje de {$platform}."; + if (isset($_GET['message'])) { + $errorMessage .= ' ' . htmlspecialchars($_GET['message']); + } + } + echo htmlspecialchars($errorMessage); + ?> +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PlataformaDestinatarioContenido (Previo)Fecha de EnvíoCreado porAcciones
No se han enviado mensajes todavía.
+ + + + + (' . $msg['recipient_type'] . ')' ?> +
+ ... +
+
+
+
+ + + +
+ + + +
+ + + + + + +
+ +
+ + + + + + +
+ + +
+
+
+
+
+
+ + + + diff --git a/templates/header.php b/templates/header.php new file mode 100755 index 0000000..06bb497 --- /dev/null +++ b/templates/header.php @@ -0,0 +1,151 @@ + + + + + + + Bot Discord + + + + + + + + + + + + + + + +
+ +
+ + \ No newline at end of file