AutoBackups/templates/dashboard.html

384 lines
16 KiB
HTML

{% extends "base.html" %}
{% block title %}Dashboard - AutoBackups{% endblock %}
{% block content %}
<div class="row">
<!-- Statistics Cards -->
<div class="col-12">
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-center bg-primary text-white">
<div class="card-body">
<h5 class="card-title">
<i class="bi bi-folder2-open"></i> Total Proyectos
</h5>
<h2 class="card-text">{{ stats.total_projects }}</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center bg-success text-white">
<div class="card-body">
<h5 class="card-title">
<i class="bi bi-check-circle"></i> Habilitados
</h5>
<h2 class="card-text">{{ stats.enabled_projects }}</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center bg-info text-white">
<div class="card-body">
<h5 class="card-title">
<i class="bi bi-hdd"></i> Espacio Libre
</h5>
<h2 class="card-text">{{ "%.1f"|format(stats.free_space_mb) }} MB</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center bg-warning text-white">
<div class="card-body">
<h5 class="card-title">
<i class="bi bi-hdd-stack"></i> Espacio Total
</h5>
<h2 class="card-text">{{ "%.1f"|format(stats.total_space_mb) }} MB</h2>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Projects Table -->
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-list-task"></i> Proyectos
</h5>
<div>
<button class="btn btn-outline-primary btn-sm" id="refresh-projects-btn">
<i class="bi bi-arrow-clockwise"></i> Actualizar
</button>
<button class="btn btn-success btn-sm" id="backup-all-btn">
<i class="bi bi-archive"></i> Backup Todos
</button>
</div>
</div>
<div class="card-body">
{% if projects %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>Proyecto</th>
<th>Ruta</th>
<th>Tipo</th>
<th>Estado</th>
<th>Último Backup</th>
<th>Próximo Backup</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
{% for project in projects %}
<tr>
<td>
<strong>{{ project.name or 'Sin nombre' }}</strong>
<br>
<small class="text-muted">ID: {{ project.id }}</small>
</td>
<td>
<span title="{{ project.path }}">
{{ project.path[:50] }}{% if project.path|length > 50 %}...{% endif %}
</span>
</td>
<td>
<span class="badge bg-secondary">{{ project.type or 'N/A' }}</span>
</td>
<td>
{% if project.current_status.value == 'ready' %}
<span class="badge bg-success">Listo</span>
{% elif project.current_status.value == 'backing_up' %}
<span class="badge bg-warning">En progreso</span>
{% elif project.current_status.value == 'error' %}
<span class="badge bg-danger">Error</span>
{% elif project.current_status.value == 'files_in_use' %}
<span class="badge bg-warning">Archivos en uso</span>
{% else %}
<span class="badge bg-secondary">{{ project.current_status.value }}</span>
{% endif %}
</td>
<td>
{% if project.last_backup_date %}
{{ project.last_backup_date[:19] }}
{% else %}
<span class="text-muted">Nunca</span>
{% endif %}
</td>
<td>
{% if project.next_scheduled_backup %}
{{ project.next_scheduled_backup[:19] }}
{% else %}
<span class="text-muted">No programado</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<button class="btn btn-outline-primary backup-project-btn"
data-project-id="{{ project.id }}">
<i class="bi bi-archive"></i>
</button>
<button class="btn btn-outline-secondary config-project-btn"
data-project-id="{{ project.id }}">
<i class="bi bi-gear"></i>
</button>
<button class="btn btn-outline-info view-project-btn"
data-project-id="{{ project.id }}">
<i class="bi bi-eye"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-folder-x display-1 text-muted"></i>
<h4 class="text-muted mt-3">No hay proyectos disponibles</h4>
<p class="text-muted">Ejecuta un escaneo para buscar proyectos en los directorios configurados.</p>
<button class="btn btn-primary" id="first-scan-btn">
<i class="bi bi-search"></i> Escanear Proyectos
</button>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block modals %}
<!-- Project Configuration Modal -->
<div class="modal fade" id="projectConfigModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Configuración del Proyecto</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="projectConfigForm">
<input type="hidden" id="modal-project-id">
<div class="mb-3">
<label for="modal-project-schedule" class="form-label">Frecuencia de Backup</label>
<select class="form-select" id="modal-project-schedule">
<option value="manual">Manual</option>
<option value="daily">Diario</option>
<option value="hourly">Cada hora</option>
<option value="3-hour">Cada 3 horas</option>
<option value="7-hour">Cada 7 horas</option>
<option value="startup">Al iniciar</option>
</select>
</div>
<div class="mb-3">
<label for="modal-project-time" class="form-label">Hora de Backup (para programación diaria)</label>
<input type="time" class="form-control" id="modal-project-time">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="modal-project-enabled">
<label class="form-check-label" for="modal-project-enabled">
Backup habilitado
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="button" class="btn btn-primary" id="save-project-config-btn">Guardar</button>
</div>
</div>
</div>
</div>
<!-- System Status Modal -->
<div class="modal fade" id="systemStatusModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Estado del Sistema</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="system-status-content">
<div class="text-center">
<div class="spinner-border" role="status">
<span class="visually-hidden">Cargando...</span>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Event listeners for project actions
document.querySelectorAll('.backup-project-btn').forEach(btn => {
btn.addEventListener('click', function() {
const projectId = this.dataset.projectId;
backupProject(projectId);
});
});
document.querySelectorAll('.config-project-btn').forEach(btn => {
btn.addEventListener('click', function() {
const projectId = this.dataset.projectId;
openProjectConfig(projectId);
});
});
// Other event listeners
const refreshBtn = document.getElementById('refresh-projects-btn');
if (refreshBtn) {
refreshBtn.addEventListener('click', () => location.reload());
}
const scanBtn = document.getElementById('scan-projects-btn');
if (scanBtn) {
scanBtn.addEventListener('click', scanProjects);
}
const firstScanBtn = document.getElementById('first-scan-btn');
if (firstScanBtn) {
firstScanBtn.addEventListener('click', scanProjects);
}
const systemStatusBtn = document.getElementById('system-status-btn');
if (systemStatusBtn) {
systemStatusBtn.addEventListener('click', showSystemStatus);
}
});
function backupProject(projectId) {
showAlert('info', `Iniciando backup del proyecto ${projectId}...`);
fetch(`/api/projects/${projectId}/backup`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.error) {
showAlert('danger', `Error: ${data.error}`);
} else {
showAlert('success', data.message || 'Backup iniciado correctamente');
}
})
.catch(error => {
showAlert('danger', `Error de conexión: ${error.message}`);
});
}
function openProjectConfig(projectId) {
document.getElementById('modal-project-id').value = projectId;
const modal = new bootstrap.Modal(document.getElementById('projectConfigModal'));
modal.show();
}
function scanProjects() {
showAlert('info', 'Escaneando directorios en busca de proyectos...');
fetch('/api/scan', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.error) {
showAlert('danger', `Error: ${data.error}`);
} else {
showAlert('success', `Escaneo completado. ${data.projects_found} proyectos encontrados.`);
setTimeout(() => location.reload(), 2000);
}
})
.catch(error => {
showAlert('danger', `Error de conexión: ${error.message}`);
});
}
function showSystemStatus() {
const modal = new bootstrap.Modal(document.getElementById('systemStatusModal'));
fetch('/api/system/status')
.then(response => response.json())
.then(data => {
if (data.error) {
document.getElementById('system-status-content').innerHTML =
`<div class="alert alert-danger">Error: ${data.error}</div>`;
} else {
document.getElementById('system-status-content').innerHTML = `
<div class="row">
<div class="col-md-6">
<h6>Espacio en Disco</h6>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between">
<span>Total:</span>
<span>${data.disk_space.total_mb.toFixed(1)} MB</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span>Usado:</span>
<span>${data.disk_space.used_mb.toFixed(1)} MB</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span>Libre:</span>
<span>${data.disk_space.free_mb.toFixed(1)} MB</span>
</li>
</ul>
</div>
<div class="col-md-6">
<h6>Proyectos</h6>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between">
<span>Total:</span>
<span>${data.projects.total}</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span>Habilitados:</span>
<span>${data.projects.enabled}</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span>Everything API:</span>
<span class="badge ${data.everything_api.available ? 'bg-success' : 'bg-warning'}">
${data.everything_api.available ? 'Disponible' : 'No disponible'}
</span>
</li>
</ul>
</div>
</div>
`;
}
})
.catch(error => {
document.getElementById('system-status-content').innerHTML =
`<div class="alert alert-danger">Error de conexión: ${error.message}</div>`;
});
modal.show();
}
</script>
{% endblock %}