Files
traduccion_bots/panel/templates/discord_channels.html
nickpons666 39f531a331 Fix Discord channel activation system
- Fix MySQL boolean conversion in toggle_channel_status
- Improve cache management with 5-second timeout
- Add bulk channel selection and toggle functionality
- Fix Jinja2 template syntax errors
- Add comprehensive debugging for channel status queries
- Implement real-time channel activation without container restart
2026-03-20 06:41:35 -06:00

271 lines
13 KiB
HTML

{% set lang = request.cookies.get('panel_lang', 'es') %}
<!DOCTYPE html>
<html lang="{{ lang }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ "Canales de Discord - Bots de Traducción" | translate(lang) }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<style>
.channel-item {
border-left: 4px solid #6f42c1;
transition: all 0.3s ease;
}
.channel-item:hover {
border-left-color: #563d7c;
background-color: #f8f9fa;
}
.channel-inactive {
border-left-color: #dc3545;
opacity: 0.7;
}
.server-card {
border: 1px solid #dee2e6;
border-radius: 8px;
margin-bottom: 20px;
}
.server-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px;
border-radius: 8px 8px 0 0;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #28a745;
}
input:checked + .slider:before {
transform: translateX(26px);
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/dashboard">
<i class="bi bi-translate"></i> {{ "Bots de Traducción" | translate(lang) }}
</a>
<div class="d-flex align-items-center">
<div class="dropdown me-3">
<button class="btn btn-outline-light btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-translate"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item {{ 'active' if lang == 'es' }}" href="/set-lang/es">Español</a></li>
<li><a class="dropdown-item {{ 'active' if lang == 'en' }}" href="/set-lang/en">English</a></li>
<li><a class="dropdown-item {{ 'active' if lang == 'pt' }}" href="/set-lang/pt">Português</a></li>
</ul>
</div>
<a href="/dashboard" class="btn btn-outline-light btn-sm me-2">{{ "Dashboard" | translate(lang) }}</a>
<a href="/logout" class="btn btn-outline-light btn-sm">{{ "Cerrar Sesión" | translate(lang) }}</a>
</div>
</div>
</nav>
<div class="container mt-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>📱 {{ "Canales de Discord" | translate(lang) }}</h2>
<div>
<a href="/diagnosis" class="btn btn-outline-secondary me-2">
<i class="bi bi-bug"></i> {{ "Diagnóstico" | translate(lang) }}
</a>
<form method="post" action="/discord-channels/sync" class="d-inline">
<button type="submit" class="btn btn-primary" onclick="return confirm('{{ '¿Sincronizar servidores y canales desde Discord?' | translate(lang) }}')">
<i class="bi bi-arrow-repeat"></i> {{ "Sincronizar" | translate(lang) }}
</button>
</form>
</div>
</div>
{% if success %}
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="bi bi-check-circle"></i> {{ "Configuración actualizada correctamente." | translate(lang) }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endif %}
{% if synced %}
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="bi bi-check-circle"></i> {{ "Servidores y canales sincronizados desde Discord." | translate(lang) }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endif %}
{% if error %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-circle"></i> {{ "Error al realizar la operación. Verifica la configuración del bot." | translate(lang) }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endif %}
{% if servers_with_channels|length == 0 %}
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> {{ "No hay servidores sincronizados. Usa el botón 'Sincronizar' para obtener los servidores y canales de Discord." | translate(lang) }}
</div>
{% else %}
{% for server_id, server_data in servers_with_channels.items() %}
<div class="card mb-4">
<div class="card-header bg-dark text-white">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-server"></i> {{ server_data.server_info.server_name }}
<small class="text-muted">({{ server_data.server_info.server_id }})</small>
</h5>
<form method="post" action="/discord-channels/delete-server" class="d-inline">
<input type="hidden" name="server_id" value="{{ server_id }}">
<button type="submit" class="btn btn-sm btn-outline-danger"
onclick="return confirm('{{ '¿Eliminar este servidor y todos sus canales?' | translate(lang) }}')">
<i class="bi bi-trash"></i> {{ "Eliminar" | translate(lang) }}
</button>
</form>
</div>
</div>
<div class="card-body">
{% if server_data.channels|length == 0 %}
<p class="text-muted">{{ "No hay canales en este servidor." | translate(lang) }}</p>
{% else %}
<form method="post" action="/discord-channels/bulk-toggle">
<input type="hidden" name="server_id" value="{{ server_id }}">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th width="50px">
<input type="checkbox" class="form-check-input" id="selectAll_{{ server_id }}"
onchange="toggleAllChannels({{ server_id }})">
</th>
<th>{{ "Canal" | translate(lang) }}</th>
<th>{{ "ID" | translate(lang) }}</th>
<th width="100px">{{ "Estado" | translate(lang) }}</th>
<th width="120px">{{ "Acciones" | translate(lang) }}</th>
</tr>
</thead>
<tbody>
{% for channel in server_data.channels %}
<tr>
<td>
<input type="checkbox" class="form-check-input channel-checkbox"
name="channel_ids" value="{{ channel.channel_id }}"
data-server="{{ server_id }}">
</td>
<td>
<i class="bi bi-hash"></i> {{ channel.channel_name }}
</td>
<td><code>{{ channel.channel_id }}</code></td>
<td>
{% if channel.is_active %}
<span class="badge bg-success">{{ "Activo" | translate(lang) }}</span>
{% else %}
<span class="badge bg-secondary">{{ "Inactivo" | translate(lang) }}</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<form method="post" action="/discord-channels/toggle" class="d-inline">
<input type="hidden" name="channel_id" value="{{ channel.channel_id }}">
<input type="hidden" name="is_active" value="{{ 'false' if channel.is_active else 'true' }}">
<button type="submit" class="btn {% if channel.is_active %}btn-warning{% else %}btn-success{% endif %} btn-sm"
title="{{ 'Desactivar' if channel.is_active else 'Activar' | translate(lang) }}">
<i class="bi bi-{% if channel.is_active %}pause{% else %}play{% endif %}"></i>
</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="mt-3">
<button type="submit" name="bulk_action" value="activate"
class="btn btn-success btn-sm me-2" disabled>
<i class="bi bi-play"></i> {{ "Activar seleccionados" | translate(lang) }}
</button>
<button type="submit" name="bulk_action" value="deactivate"
class="btn btn-warning btn-sm" disabled>
<i class="bi bi-pause"></i> {{ "Desactivar seleccionados" | translate(lang) }}
</button>
</div>
</form>
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function toggleAllChannels(serverId) {
const selectAll = document.getElementById('selectAll_' + serverId);
const checkboxes = document.querySelectorAll('.channel-checkbox[data-server="' + serverId + '"]');
const bulkButtons = selectAll.closest('form').querySelectorAll('button[type="submit"][name="bulk_action"]');
checkboxes.forEach(checkbox => {
checkbox.checked = selectAll.checked;
});
// Habilitar/deshabilitar botones bulk
const anyChecked = document.querySelectorAll('.channel-checkbox:checked').length > 0;
bulkButtons.forEach(btn => btn.disabled = !anyChecked);
}
function updateBulkButtons() {
const anyChecked = document.querySelectorAll('.channel-checkbox:checked').length > 0;
document.querySelectorAll('button[type="submit"][name="bulk_action"]').forEach(btn => {
btn.disabled = !anyChecked;
});
// Actualizar checkboxes de "seleccionar todo"
document.querySelectorAll('[id^="selectAll_"]').forEach(selectAll => {
const serverId = selectAll.id.replace('selectAll_', '');
const checkboxes = document.querySelectorAll('.channel-checkbox[data-server="' + serverId + '"]');
const allChecked = Array.from(checkboxes).every(cb => cb.checked);
const anyChecked = Array.from(checkboxes).some(cb => cb.checked);
selectAll.checked = allChecked;
selectAll.indeterminate = anyChecked && !allChecked;
});
}
// Agregar event listeners a todos los checkboxes
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.channel-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', updateBulkButtons);
});
});
</script>
</body>
</html>