w
This commit is contained in:
@@ -0,0 +1,694 @@
|
||||
/*
|
||||
* WhatsApp Bot Manager - Estilos CSS
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
:root {
|
||||
--primary-color: #25d366;
|
||||
--secondary-color: #128c7e;
|
||||
--tertiary-color: #075e54;
|
||||
--accent-color: #dcf8c6;
|
||||
--dark-bg: #1f2937;
|
||||
--light-bg: #f9fafb;
|
||||
--border-color: #e5e7eb;
|
||||
--text-primary: #1f2937;
|
||||
--text-secondary: #6b7280;
|
||||
--success-color: #10b981;
|
||||
--warning-color: #f59e0b;
|
||||
--error-color: #ef4444;
|
||||
--info-color: #3b82f6;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 280px;
|
||||
height: 100vh;
|
||||
background: linear-gradient(135deg, var(--tertiary-color), var(--secondary-color));
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.sidebar-header h4 {
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar-menu li {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.sidebar-menu .nav-link {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-menu .nav-link::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: var(--primary-color);
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-menu .nav-link:hover,
|
||||
.sidebar-menu .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.sidebar-menu .nav-link.active::before,
|
||||
.sidebar-menu .nav-link:hover::before {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.sidebar-menu .nav-link i {
|
||||
margin-right: 10px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
background: white;
|
||||
padding: 20px 30px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.content-header h1 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-indicator.online {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.status-indicator i {
|
||||
font-size: 0.7rem;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* Tab Content */
|
||||
.tab-content {
|
||||
display: none;
|
||||
padding: 30px;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07);
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: var(--light-bg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 20px;
|
||||
border-radius: 12px 12px 0 0;
|
||||
}
|
||||
|
||||
.card-header h5 {
|
||||
margin: 0;
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Stat Cards */
|
||||
.card-stat {
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card-stat:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.card-stat-title {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.card-stat-number {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
padding: 10px 20px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
.btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, var(--secondary-color), var(--tertiary-color));
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(37, 211, 102, 0.4);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: var(--success-color);
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: var(--warning-color);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: var(--error-color);
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background: var(--info-color);
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
.form-control {
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 12px 15px;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 0.2rem rgba(37, 211, 102, 0.25);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: var(--light-bg);
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
padding: 15px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 15px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table-hover tbody tr:hover {
|
||||
background-color: rgba(37, 211, 102, 0.05);
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
.badge {
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: var(--success-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: var(--warning-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background: var(--error-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
background: var(--info-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-secondary {
|
||||
background: var(--text-secondary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Menu Tree */
|
||||
.menu-tree {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.menu-tree li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
justify-content: between;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
border-color: var(--primary-color);
|
||||
background: rgba(37, 211, 102, 0.05);
|
||||
}
|
||||
|
||||
.menu-item.root {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.menu-item.child {
|
||||
margin-left: 20px;
|
||||
border-left: 4px solid var(--info-color);
|
||||
}
|
||||
|
||||
/* Recent Messages */
|
||||
.recent-message {
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 4px solid var(--primary-color);
|
||||
background: rgba(37, 211, 102, 0.05);
|
||||
}
|
||||
|
||||
.recent-message .time {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.recent-message .content {
|
||||
font-size: 0.9rem;
|
||||
margin-top: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Loading Spinner */
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: white;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Alerts */
|
||||
.alert {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-left: 4px solid var(--success-color);
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border-left: 4px solid var(--warning-color);
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-left: 4px solid var(--error-color);
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-left: 4px solid var(--info-color);
|
||||
color: var(--info-color);
|
||||
}
|
||||
|
||||
/* Chat Interface */
|
||||
.chat-container {
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
background: #f0f0f0;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.message.incoming {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.message.outgoing {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
max-width: 70%;
|
||||
padding: 10px 15px;
|
||||
border-radius: 18px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.message.incoming .message-bubble {
|
||||
background: white;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.message.outgoing .message-bubble {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.7;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.sidebar.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.card-stat-number {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Mode Support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--light-bg: #1f2937;
|
||||
--text-primary: #f9fafb;
|
||||
--text-secondary: #d1d5db;
|
||||
--border-color: #374151;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--dark-bg);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #374151;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: #4b5563;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #4b5563;
|
||||
}
|
||||
}
|
||||
|
||||
/* Utility Classes */
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
.text-left { text-align: left; }
|
||||
|
||||
.mb-0 { margin-bottom: 0; }
|
||||
.mb-1 { margin-bottom: 0.25rem; }
|
||||
.mb-2 { margin-bottom: 0.5rem; }
|
||||
.mb-3 { margin-bottom: 1rem; }
|
||||
.mb-4 { margin-bottom: 1.5rem; }
|
||||
.mb-5 { margin-bottom: 3rem; }
|
||||
|
||||
.mt-0 { margin-top: 0; }
|
||||
.mt-1 { margin-top: 0.25rem; }
|
||||
.mt-2 { margin-top: 0.5rem; }
|
||||
.mt-3 { margin-top: 1rem; }
|
||||
.mt-4 { margin-top: 1.5rem; }
|
||||
.mt-5 { margin-top: 3rem; }
|
||||
|
||||
.d-flex { display: flex; }
|
||||
.d-block { display: block; }
|
||||
.d-none { display: none; }
|
||||
|
||||
.justify-content-between { justify-content: space-between; }
|
||||
.justify-content-center { justify-content: center; }
|
||||
.justify-content-end { justify-content: flex-end; }
|
||||
|
||||
.align-items-center { align-items: center; }
|
||||
.align-items-start { align-items: flex-start; }
|
||||
.align-items-end { align-items: flex-end; }
|
||||
|
||||
.w-100 { width: 100%; }
|
||||
.h-100 { height: 100%; }
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--text-secondary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Animation Classes */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-up {
|
||||
animation: slideUp 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.bounce {
|
||||
animation: bounce 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
|
||||
40% { transform: translateY(-10px); }
|
||||
60% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
/* Custom Components */
|
||||
.progress-ring {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.progress-ring__circle {
|
||||
stroke: var(--primary-color);
|
||||
stroke-linecap: round;
|
||||
transition: stroke-dasharray 0.35s;
|
||||
}
|
||||
|
||||
.notification-dot {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: var(--error-color);
|
||||
border-radius: 50%;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.sidebar,
|
||||
.header-actions,
|
||||
.btn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,912 @@
|
||||
/**
|
||||
* WhatsApp Bot Manager - JavaScript
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
class WhatsAppBotManager {
|
||||
constructor() {
|
||||
this.apiBaseUrl = './api/';
|
||||
this.currentTab = 'dashboard';
|
||||
this.currentMenuId = null;
|
||||
this.charts = {};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setupEventListeners();
|
||||
this.loadDashboard();
|
||||
this.setupCharts();
|
||||
|
||||
// Auto-refresh cada 30 segundos
|
||||
setInterval(() => {
|
||||
if (this.currentTab === 'dashboard') {
|
||||
this.refreshStats();
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Navegación de tabs
|
||||
document.querySelectorAll('.nav-link').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const tabName = link.getAttribute('data-tab');
|
||||
this.showTab(tabName);
|
||||
});
|
||||
});
|
||||
|
||||
// Formularios
|
||||
this.setupFormListeners();
|
||||
|
||||
// Búsquedas
|
||||
this.setupSearchListeners();
|
||||
}
|
||||
|
||||
setupFormListeners() {
|
||||
// Formulario de configuración
|
||||
const settingsForm = document.getElementById('settings-form');
|
||||
if (settingsForm) {
|
||||
settingsForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.saveSettings();
|
||||
});
|
||||
}
|
||||
|
||||
// Formulario de menú
|
||||
const menuForm = document.getElementById('menu-form');
|
||||
if (menuForm) {
|
||||
menuForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.saveMenu();
|
||||
});
|
||||
}
|
||||
|
||||
// Formulario de envío de mensaje
|
||||
const sendMessageForm = document.getElementById('send-message-form');
|
||||
if (sendMessageForm) {
|
||||
sendMessageForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.sendMessage();
|
||||
});
|
||||
}
|
||||
|
||||
// Formulario de mensaje masivo
|
||||
const broadcastForm = document.getElementById('broadcast-form');
|
||||
if (broadcastForm) {
|
||||
broadcastForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.sendBroadcast();
|
||||
});
|
||||
}
|
||||
|
||||
// Cambio de tipo de mensaje
|
||||
const messageType = document.getElementById('message-type');
|
||||
if (messageType) {
|
||||
messageType.addEventListener('change', () => {
|
||||
this.toggleMessageFields();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setupSearchListeners() {
|
||||
// Búsqueda de conversaciones
|
||||
const searchConversations = document.getElementById('search-conversations');
|
||||
if (searchConversations) {
|
||||
searchConversations.addEventListener('input', (e) => {
|
||||
this.searchConversations(e.target.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showTab(tabName) {
|
||||
// Ocultar todas las pestañas
|
||||
document.querySelectorAll('.tab-content').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
|
||||
// Remover clase active de navegación
|
||||
document.querySelectorAll('.nav-link').forEach(link => {
|
||||
link.classList.remove('active');
|
||||
});
|
||||
|
||||
// Mostrar pestaña seleccionada
|
||||
const targetTab = document.getElementById(tabName);
|
||||
if (targetTab) {
|
||||
targetTab.classList.add('active');
|
||||
}
|
||||
|
||||
// Activar enlace de navegación
|
||||
const activeLink = document.querySelector(`[data-tab="${tabName}"]`);
|
||||
if (activeLink) {
|
||||
activeLink.classList.add('active');
|
||||
}
|
||||
|
||||
this.currentTab = tabName;
|
||||
|
||||
// Cargar datos específicos del tab
|
||||
this.loadTabData(tabName);
|
||||
}
|
||||
|
||||
loadTabData(tabName) {
|
||||
switch (tabName) {
|
||||
case 'dashboard':
|
||||
this.loadDashboard();
|
||||
break;
|
||||
case 'conversations':
|
||||
this.loadConversations();
|
||||
break;
|
||||
case 'users':
|
||||
this.loadUsers();
|
||||
break;
|
||||
case 'menus':
|
||||
this.loadMenus();
|
||||
break;
|
||||
case 'messages':
|
||||
this.loadMessageUsers();
|
||||
break;
|
||||
case 'templates':
|
||||
this.loadTemplates();
|
||||
break;
|
||||
case 'autoresponses':
|
||||
this.loadAutoResponses();
|
||||
break;
|
||||
case 'settings':
|
||||
this.loadSettings();
|
||||
break;
|
||||
case 'logs':
|
||||
this.loadLogs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async loadDashboard() {
|
||||
try {
|
||||
const stats = await this.apiCall('get_stats.php');
|
||||
this.updateStats(stats);
|
||||
|
||||
const recentMessages = await this.apiCall('get_recent_messages.php');
|
||||
this.updateRecentMessages(recentMessages);
|
||||
|
||||
this.updateChart();
|
||||
} catch (error) {
|
||||
this.showError('Error cargando dashboard: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
updateStats(stats) {
|
||||
if (stats) {
|
||||
document.getElementById('total-users').textContent = stats.total_users || 0;
|
||||
document.getElementById('messages-today').textContent = stats.messages_today || 0;
|
||||
document.getElementById('active-users').textContent = stats.active_users || 0;
|
||||
document.getElementById('total-messages').textContent = stats.total_messages || 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateRecentMessages(messages) {
|
||||
const container = document.getElementById('recent-messages');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
if (messages && messages.length > 0) {
|
||||
messages.forEach(message => {
|
||||
const messageElement = this.createRecentMessageElement(message);
|
||||
container.appendChild(messageElement);
|
||||
});
|
||||
} else {
|
||||
container.innerHTML = '<div class="text-center text-muted">No hay mensajes recientes</div>';
|
||||
}
|
||||
}
|
||||
|
||||
createRecentMessageElement(message) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'recent-message';
|
||||
|
||||
const direction = message.direction === 'incoming' ? '📥' : '📤';
|
||||
const time = new Date(message.created_at).toLocaleString();
|
||||
|
||||
div.innerHTML = `
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="fw-bold">${direction} ${message.phone_number}</span>
|
||||
<span class="time">${time}</span>
|
||||
</div>
|
||||
<div class="content">${this.truncateText(message.content, 50)}</div>
|
||||
`;
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
setupCharts() {
|
||||
const ctx = document.getElementById('messagesChart');
|
||||
if (ctx) {
|
||||
this.charts.messages = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Mensajes',
|
||||
data: [],
|
||||
borderColor: '#25d366',
|
||||
backgroundColor: 'rgba(37, 211, 102, 0.1)',
|
||||
borderWidth: 3,
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0,0,0,0.1)'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
color: 'rgba(0,0,0,0.1)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async updateChart() {
|
||||
try {
|
||||
const chartData = await this.apiCall('get_chart_data.php');
|
||||
if (chartData && this.charts.messages) {
|
||||
this.charts.messages.data.labels = chartData.labels;
|
||||
this.charts.messages.data.datasets[0].data = chartData.data;
|
||||
this.charts.messages.update();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating chart:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async loadConversations() {
|
||||
try {
|
||||
const conversations = await this.apiCall('get_conversations.php');
|
||||
this.updateConversationsTable(conversations);
|
||||
} catch (error) {
|
||||
this.showError('Error cargando conversaciones: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
updateConversationsTable(conversations) {
|
||||
const tbody = document.getElementById('conversations-table');
|
||||
if (!tbody) return;
|
||||
|
||||
tbody.innerHTML = '';
|
||||
|
||||
conversations.forEach(conv => {
|
||||
const row = this.createConversationRow(conv);
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
createConversationRow(conv) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td>
|
||||
<div class="fw-bold">${conv.phone_number}</div>
|
||||
<small class="text-muted">${conv.name || 'Sin nombre'}</small>
|
||||
</td>
|
||||
<td>${this.truncateText(conv.last_message, 50)}</td>
|
||||
<td>
|
||||
<span class="badge badge-${this.getMessageTypeColor(conv.message_type)}">
|
||||
${conv.message_type}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-${this.getStatusColor(conv.status)}">
|
||||
${conv.status}
|
||||
</span>
|
||||
</td>
|
||||
<td>${new Date(conv.last_activity).toLocaleString()}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" onclick="app.viewConversation('${conv.user_id}')">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-success" onclick="app.replyToUser('${conv.phone_number}')">
|
||||
<i class="fas fa-reply"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
return tr;
|
||||
}
|
||||
|
||||
async loadUsers() {
|
||||
try {
|
||||
const users = await this.apiCall('get_users.php');
|
||||
this.updateUsersTable(users);
|
||||
} catch (error) {
|
||||
this.showError('Error cargando usuarios: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
updateUsersTable(users) {
|
||||
const tbody = document.getElementById('users-table');
|
||||
if (!tbody) return;
|
||||
|
||||
tbody.innerHTML = '';
|
||||
|
||||
users.forEach(user => {
|
||||
const row = this.createUserRow(user);
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
createUserRow(user) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td>${user.id}</td>
|
||||
<td>${user.phone_number}</td>
|
||||
<td>${user.name || 'Sin nombre'}</td>
|
||||
<td>
|
||||
<span class="badge badge-${this.getStatusColor(user.status)}">
|
||||
${user.status}
|
||||
</span>
|
||||
</td>
|
||||
<td>${user.current_menu || 'Ninguno'}</td>
|
||||
<td>${new Date(user.created_at).toLocaleString()}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" onclick="app.editUser(${user.id})">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning" onclick="app.blockUser(${user.id})">
|
||||
<i class="fas fa-ban"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
return tr;
|
||||
}
|
||||
|
||||
async loadMenus() {
|
||||
try {
|
||||
const menus = await this.apiCall('get_menus.php');
|
||||
this.updateMenusTree(menus);
|
||||
this.loadMenuParents(menus);
|
||||
} catch (error) {
|
||||
this.showError('Error cargando menús: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
updateMenusTree(menus) {
|
||||
const container = document.getElementById('menus-tree');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
// Organizar menús por jerarquía
|
||||
const rootMenus = menus.filter(menu => menu.is_root);
|
||||
|
||||
rootMenus.forEach(menu => {
|
||||
const menuElement = this.createMenuElement(menu, menus);
|
||||
container.appendChild(menuElement);
|
||||
});
|
||||
}
|
||||
|
||||
createMenuElement(menu, allMenus) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'menu-item ' + (menu.is_root ? 'root' : 'child');
|
||||
|
||||
div.innerHTML = `
|
||||
<div>
|
||||
<h6 class="mb-1">${menu.title}</h6>
|
||||
<small class="text-muted">${menu.description || 'Sin descripción'}</small>
|
||||
<div class="mt-2">
|
||||
<span class="badge badge-${menu.is_active ? 'success' : 'secondary'}">
|
||||
${menu.is_active ? 'Activo' : 'Inactivo'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-primary me-2" onclick="app.editMenu(${menu.id})">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-info me-2" onclick="app.manageMenuOptions(${menu.id})">
|
||||
<i class="fas fa-list"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" onclick="app.deleteMenu(${menu.id})">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Agregar submenús
|
||||
const childMenus = allMenus.filter(child => child.parent_id === menu.id);
|
||||
if (childMenus.length > 0) {
|
||||
const childContainer = document.createElement('div');
|
||||
childContainer.style.marginLeft = '20px';
|
||||
childContainer.style.marginTop = '10px';
|
||||
|
||||
childMenus.forEach(child => {
|
||||
const childElement = this.createMenuElement(child, allMenus);
|
||||
childContainer.appendChild(childElement);
|
||||
});
|
||||
|
||||
div.appendChild(childContainer);
|
||||
}
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
loadMenuParents(menus) {
|
||||
const select = document.getElementById('menu-parent');
|
||||
if (!select) return;
|
||||
|
||||
select.innerHTML = '<option value="">Sin padre (Menú raíz)</option>';
|
||||
|
||||
menus.forEach(menu => {
|
||||
const option = document.createElement('option');
|
||||
option.value = menu.id;
|
||||
option.textContent = menu.title;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
async loadMessageUsers() {
|
||||
try {
|
||||
const users = await this.apiCall('get_users.php');
|
||||
const templates = await this.apiCall('get_templates.php');
|
||||
|
||||
this.updateMessageUserSelect(users);
|
||||
this.updateTemplateSelect(templates);
|
||||
} catch (error) {
|
||||
this.showError('Error cargando datos de mensaje: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
updateMessageUserSelect(users) {
|
||||
const select = document.getElementById('message-recipient');
|
||||
if (!select) return;
|
||||
|
||||
select.innerHTML = '<option value="">Seleccionar usuario...</option>';
|
||||
|
||||
users.forEach(user => {
|
||||
const option = document.createElement('option');
|
||||
option.value = user.phone_number;
|
||||
option.textContent = `${user.phone_number} ${user.name ? '(' + user.name + ')' : ''}`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
updateTemplateSelect(templates) {
|
||||
const select = document.getElementById('message-template');
|
||||
if (!select) return;
|
||||
|
||||
select.innerHTML = '<option value="">Seleccionar plantilla...</option>';
|
||||
|
||||
templates.forEach(template => {
|
||||
if (template.status === 'approved') {
|
||||
const option = document.createElement('option');
|
||||
option.value = template.template_name;
|
||||
option.textContent = template.name;
|
||||
select.appendChild(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleMessageFields() {
|
||||
const messageType = document.getElementById('message-type').value;
|
||||
const textGroup = document.getElementById('message-text-group');
|
||||
const templateGroup = document.getElementById('template-group');
|
||||
|
||||
if (messageType === 'template') {
|
||||
textGroup.style.display = 'none';
|
||||
templateGroup.style.display = 'block';
|
||||
} else {
|
||||
textGroup.style.display = 'block';
|
||||
templateGroup.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
async sendMessage() {
|
||||
const recipient = document.getElementById('message-recipient').value;
|
||||
const messageType = document.getElementById('message-type').value;
|
||||
const messageText = document.getElementById('message-text').value;
|
||||
const template = document.getElementById('message-template').value;
|
||||
|
||||
if (!recipient) {
|
||||
this.showError('Por favor selecciona un destinatario');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
recipient: recipient,
|
||||
type: messageType
|
||||
};
|
||||
|
||||
if (messageType === 'text') {
|
||||
if (!messageText) {
|
||||
this.showError('Por favor escribe un mensaje');
|
||||
return;
|
||||
}
|
||||
data.message = messageText;
|
||||
} else if (messageType === 'template') {
|
||||
if (!template) {
|
||||
this.showError('Por favor selecciona una plantilla');
|
||||
return;
|
||||
}
|
||||
data.template = template;
|
||||
}
|
||||
|
||||
try {
|
||||
this.showLoading('Enviando mensaje...');
|
||||
const result = await this.apiCall('send_message.php', 'POST', data);
|
||||
|
||||
if (result.success) {
|
||||
this.showSuccess('Mensaje enviado correctamente');
|
||||
document.getElementById('send-message-form').reset();
|
||||
} else {
|
||||
this.showError(result.error || 'Error enviando mensaje');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('Error enviando mensaje: ' + error.message);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
async sendBroadcast() {
|
||||
const filter = document.getElementById('broadcast-filter').value;
|
||||
const message = document.getElementById('broadcast-message').value;
|
||||
|
||||
if (!message) {
|
||||
this.showError('Por favor escribe un mensaje');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('¿Estás seguro de enviar este mensaje a múltiples usuarios?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.showLoading('Enviando mensajes...');
|
||||
const result = await this.apiCall('send_broadcast.php', 'POST', {
|
||||
filter: filter,
|
||||
message: message
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
this.showSuccess(`Mensajes enviados a ${result.sent_count} usuarios`);
|
||||
document.getElementById('broadcast-form').reset();
|
||||
} else {
|
||||
this.showError(result.error || 'Error en envío masivo');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('Error en envío masivo: ' + error.message);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
async saveMenu() {
|
||||
const formData = {
|
||||
name: document.getElementById('menu-name').value,
|
||||
title: document.getElementById('menu-title').value,
|
||||
description: document.getElementById('menu-description').value,
|
||||
parent_id: document.getElementById('menu-parent').value || null,
|
||||
is_active: document.getElementById('menu-active').checked ? 1 : 0
|
||||
};
|
||||
|
||||
try {
|
||||
this.showLoading('Guardando menú...');
|
||||
const result = await this.apiCall('save_menu.php', 'POST', formData);
|
||||
|
||||
if (result.success) {
|
||||
this.showSuccess('Menú guardado correctamente');
|
||||
document.getElementById('menu-form').reset();
|
||||
this.loadMenus();
|
||||
} else {
|
||||
this.showError(result.error || 'Error guardando menú');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('Error guardando menú: ' + error.message);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
const formData = {
|
||||
whatsapp_token: document.getElementById('whatsapp-token').value,
|
||||
phone_number_id: document.getElementById('phone-number-id').value,
|
||||
webhook_token: document.getElementById('webhook-token').value,
|
||||
business_name: document.getElementById('business-name').value,
|
||||
welcome_message: document.getElementById('welcome-message').value
|
||||
};
|
||||
|
||||
try {
|
||||
this.showLoading('Guardando configuración...');
|
||||
const result = await this.apiCall('save_settings.php', 'POST', formData);
|
||||
|
||||
if (result.success) {
|
||||
this.showSuccess('Configuración guardada correctamente');
|
||||
} else {
|
||||
this.showError(result.error || 'Error guardando configuración');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('Error guardando configuración: ' + error.message);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
try {
|
||||
const settings = await this.apiCall('get_settings.php');
|
||||
|
||||
if (settings) {
|
||||
document.getElementById('whatsapp-token').value = settings.whatsapp_token || '';
|
||||
document.getElementById('phone-number-id').value = settings.phone_number_id || '';
|
||||
document.getElementById('webhook-token').value = settings.webhook_token || '';
|
||||
document.getElementById('business-name').value = settings.business_name || '';
|
||||
document.getElementById('welcome-message').value = settings.welcome_message || '';
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('Error cargando configuración: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async loadLogs() {
|
||||
try {
|
||||
const logs = await this.apiCall('get_logs.php');
|
||||
this.updateLogsTable(logs);
|
||||
} catch (error) {
|
||||
this.showError('Error cargando logs: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
updateLogsTable(logs) {
|
||||
const tbody = document.getElementById('logs-table');
|
||||
if (!tbody) return;
|
||||
|
||||
tbody.innerHTML = '';
|
||||
|
||||
logs.forEach(log => {
|
||||
const row = this.createLogRow(log);
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
createLogRow(log) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td>${new Date(log.created_at).toLocaleString()}</td>
|
||||
<td>${log.ip_address}</td>
|
||||
<td>
|
||||
<span class="badge badge-${log.status_code === 200 ? 'success' : 'danger'}">
|
||||
${log.status_code}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="app.viewLogDetails('${log.id}', 'request')">
|
||||
Ver Request
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="app.viewLogDetails('${log.id}', 'response')">
|
||||
Ver Response
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
return tr;
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
async apiCall(endpoint, method = 'GET', data = null) {
|
||||
const options = {
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
};
|
||||
|
||||
if (data && method !== 'GET') {
|
||||
options.body = JSON.stringify(data);
|
||||
}
|
||||
|
||||
const response = await fetch(this.apiBaseUrl + endpoint, options);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
this.showNotification(message, 'error');
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
this.showNotification(message, 'success');
|
||||
}
|
||||
|
||||
showWarning(message) {
|
||||
this.showNotification(message, 'warning');
|
||||
}
|
||||
|
||||
showInfo(message) {
|
||||
this.showNotification(message, 'info');
|
||||
}
|
||||
|
||||
showNotification(message, type = 'info') {
|
||||
// Crear notificación toast
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `alert alert-${type} position-fixed`;
|
||||
toast.style.top = '20px';
|
||||
toast.style.right = '20px';
|
||||
toast.style.zIndex = '9999';
|
||||
toast.style.minWidth = '300px';
|
||||
toast.innerHTML = `
|
||||
<i class="fas fa-${this.getNotificationIcon(type)}"></i>
|
||||
${message}
|
||||
<button type="button" class="btn-close" onclick="this.parentElement.remove()"></button>
|
||||
`;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// Auto-remove después de 5 segundos
|
||||
setTimeout(() => {
|
||||
if (toast.parentElement) {
|
||||
toast.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
getNotificationIcon(type) {
|
||||
const icons = {
|
||||
success: 'check-circle',
|
||||
error: 'exclamation-circle',
|
||||
warning: 'exclamation-triangle',
|
||||
info: 'info-circle'
|
||||
};
|
||||
return icons[type] || 'info-circle';
|
||||
}
|
||||
|
||||
showLoading(message = 'Cargando...') {
|
||||
const loading = document.createElement('div');
|
||||
loading.id = 'loading-overlay';
|
||||
loading.className = 'position-fixed w-100 h-100 d-flex align-items-center justify-content-center';
|
||||
loading.style.top = '0';
|
||||
loading.style.left = '0';
|
||||
loading.style.backgroundColor = 'rgba(0,0,0,0.5)';
|
||||
loading.style.zIndex = '9999';
|
||||
loading.innerHTML = `
|
||||
<div class="bg-white p-4 rounded text-center">
|
||||
<div class="spinner mb-3"></div>
|
||||
<div>${message}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(loading);
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
const loading = document.getElementById('loading-overlay');
|
||||
if (loading) {
|
||||
loading.remove();
|
||||
}
|
||||
}
|
||||
|
||||
truncateText(text, length) {
|
||||
if (!text) return '';
|
||||
return text.length > length ? text.substring(0, length) + '...' : text;
|
||||
}
|
||||
|
||||
getStatusColor(status) {
|
||||
const colors = {
|
||||
'active': 'success',
|
||||
'inactive': 'secondary',
|
||||
'blocked': 'danger',
|
||||
'sent': 'info',
|
||||
'delivered': 'success',
|
||||
'read': 'success',
|
||||
'failed': 'danger'
|
||||
};
|
||||
return colors[status] || 'secondary';
|
||||
}
|
||||
|
||||
getMessageTypeColor(type) {
|
||||
const colors = {
|
||||
'text': 'primary',
|
||||
'image': 'info',
|
||||
'audio': 'warning',
|
||||
'video': 'danger',
|
||||
'document': 'secondary',
|
||||
'template': 'success'
|
||||
};
|
||||
return colors[type] || 'primary';
|
||||
}
|
||||
|
||||
// Funciones específicas
|
||||
refreshData() {
|
||||
this.loadTabData(this.currentTab);
|
||||
this.showSuccess('Datos actualizados');
|
||||
}
|
||||
|
||||
refreshStats() {
|
||||
this.loadDashboard();
|
||||
}
|
||||
|
||||
exportUsers() {
|
||||
window.open(this.apiBaseUrl + 'export_users.php', '_blank');
|
||||
}
|
||||
|
||||
viewConversation(userId) {
|
||||
// Implementar modal o página de conversación
|
||||
console.log('Ver conversación del usuario:', userId);
|
||||
}
|
||||
|
||||
replyToUser(phoneNumber) {
|
||||
document.getElementById('message-recipient').value = phoneNumber;
|
||||
this.showTab('messages');
|
||||
}
|
||||
|
||||
editUser(userId) {
|
||||
// Implementar modal de edición de usuario
|
||||
console.log('Editar usuario:', userId);
|
||||
}
|
||||
|
||||
blockUser(userId) {
|
||||
if (confirm('¿Estás seguro de bloquear este usuario?')) {
|
||||
// Implementar bloqueo de usuario
|
||||
console.log('Bloquear usuario:', userId);
|
||||
}
|
||||
}
|
||||
|
||||
editMenu(menuId) {
|
||||
this.currentMenuId = menuId;
|
||||
// Cargar datos del menú en el formulario
|
||||
console.log('Editar menú:', menuId);
|
||||
}
|
||||
|
||||
deleteMenu(menuId) {
|
||||
if (confirm('¿Estás seguro de eliminar este menú?')) {
|
||||
// Implementar eliminación
|
||||
console.log('Eliminar menú:', menuId);
|
||||
}
|
||||
}
|
||||
|
||||
manageMenuOptions(menuId) {
|
||||
// Mostrar modal de opciones de menú
|
||||
console.log('Gestionar opciones del menú:', menuId);
|
||||
}
|
||||
|
||||
viewLogDetails(logId, type) {
|
||||
// Mostrar detalles del log en modal
|
||||
console.log('Ver detalles del log:', logId, type);
|
||||
}
|
||||
|
||||
clearLogs() {
|
||||
if (confirm('¿Estás seguro de eliminar todos los logs?')) {
|
||||
// Implementar limpieza de logs
|
||||
console.log('Limpiar logs');
|
||||
}
|
||||
}
|
||||
|
||||
refreshLogs() {
|
||||
this.loadLogs();
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializar la aplicación
|
||||
const app = new WhatsAppBotManager();
|
||||
|
||||
// Funciones globales
|
||||
window.refreshData = () => app.refreshData();
|
||||
window.exportUsers = () => app.exportUsers();
|
||||
window.refreshLogs = () => app.refreshLogs();
|
||||
window.clearLogs = () => app.clearLogs();
|
||||
Reference in New Issue
Block a user