ParamManagerScripts/templates/log_viewer.html

192 lines
7.9 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Visor de Log Completo - Script Parameter Manager</title>
<link rel="icon" type="image/png" href="{{ url_for('static', filename='icon.png') }}">
<script src="https://cdn.tailwindcss.com"></script>
<style>
.log-content {
font-family: 'Courier New', monospace;
white-space: pre-wrap;
word-break: break-all;
}
.search-highlight {
background-color: yellow;
padding: 1px 2px;
}
</style>
</head>
<body class="bg-gray-100">
<div class="container mx-auto p-6">
<!-- Header -->
<div class="bg-white rounded-lg shadow mb-6 p-6">
<div class="flex justify-between items-center mb-4">
<h1 class="text-2xl font-bold text-gray-800">Visor de Log Completo</h1>
<div class="flex gap-3">
<button onclick="downloadLog()"
class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
📥 Descargar
</button>
<button onclick="refreshLog()"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
🔄 Actualizar
</button>
<button onclick="window.close()"
class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
✖️ Cerrar
</button>
</div>
</div>
<!-- Search -->
<div class="mb-4">
<div class="flex gap-2">
<input type="text" id="searchInput" placeholder="Buscar en el log..."
class="flex-1 p-2 border border-gray-300 rounded focus:border-blue-500 focus:outline-none">
<button onclick="searchInLog()"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
🔍 Buscar
</button>
<button onclick="clearSearch()"
class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
Limpiar
</button>
</div>
<div id="searchStats" class="text-sm text-gray-600 mt-2"></div>
</div>
</div>
<!-- Log Content -->
<div class="bg-white rounded-lg shadow">
<div class="p-4 border-b bg-gray-50">
<div class="flex justify-between items-center">
<h2 class="text-lg font-semibold">Contenido del Log</h2>
<div class="text-sm text-gray-600">
<span id="lineCount">Calculando líneas...</span>
</div>
</div>
</div>
<div class="p-6">
<div id="logContent" class="log-content bg-gray-100 p-4 rounded border max-h-[70vh] overflow-auto text-sm">
{{ log_content }}
</div>
</div>
</div>
</div>
<script>
let originalLogContent = '';
// Inicializar al cargar la página
document.addEventListener('DOMContentLoaded', function() {
const logContentElement = document.getElementById('logContent');
originalLogContent = logContentElement.textContent;
updateLineCount();
// Scroll al final del log
logContentElement.scrollTop = logContentElement.scrollHeight;
// Configurar búsqueda con Enter
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
searchInLog();
}
});
});
function updateLineCount() {
const lines = originalLogContent.split('\n').length;
document.getElementById('lineCount').textContent = `${lines.toLocaleString()} líneas`;
}
function searchInLog() {
const searchTerm = document.getElementById('searchInput').value.trim();
const logContentElement = document.getElementById('logContent');
const searchStatsElement = document.getElementById('searchStats');
if (!searchTerm) {
clearSearch();
return;
}
// Escapar caracteres especiales para regex
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(escapedTerm, 'gi');
// Contar coincidencias
const matches = (originalLogContent.match(regex) || []).length;
if (matches > 0) {
// Resaltar coincidencias
const highlightedContent = originalLogContent.replace(regex,
'<span class="search-highlight">$&</span>');
logContentElement.innerHTML = highlightedContent;
searchStatsElement.textContent = `Encontradas ${matches} coincidencias de "${searchTerm}"`;
searchStatsElement.className = 'text-sm text-green-600 mt-2';
// Scroll a la primera coincidencia
const firstHighlight = logContentElement.querySelector('.search-highlight');
if (firstHighlight) {
firstHighlight.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
} else {
searchStatsElement.textContent = `No se encontraron coincidencias de "${searchTerm}"`;
searchStatsElement.className = 'text-sm text-red-600 mt-2';
}
}
function clearSearch() {
const logContentElement = document.getElementById('logContent');
const searchStatsElement = document.getElementById('searchStats');
// Restaurar contenido original
logContentElement.textContent = originalLogContent;
document.getElementById('searchInput').value = '';
searchStatsElement.textContent = '';
}
async function refreshLog() {
try {
const response = await fetch('/api/full-log');
const result = await response.json();
if (result.logs !== undefined) {
originalLogContent = result.logs;
document.getElementById('logContent').textContent = originalLogContent;
updateLineCount();
clearSearch();
// Scroll al final
const logContentElement = document.getElementById('logContent');
logContentElement.scrollTop = logContentElement.scrollHeight;
} else {
alert('Error al actualizar el log: ' + (result.error || 'Error desconocido'));
}
} catch (error) {
console.error('Error refreshing log:', error);
alert('Error de red al actualizar el log');
}
}
function downloadLog() {
const blob = new Blob([originalLogContent], { type: 'text/plain' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
const now = new Date();
const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, -5);
a.href = url;
a.download = `log_${timestamp}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
</script>
</body>
</html>