Initial project commit: Docker + SvelteKit source
This commit is contained in:
346
src/routes/+page.svelte
Normal file
346
src/routes/+page.svelte
Normal file
@@ -0,0 +1,346 @@
|
||||
<script lang="ts">
|
||||
import Navbar from '$lib/components/Navbar.svelte';
|
||||
import Sidebar from '$lib/components/Sidebar.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let { data } = $props();
|
||||
let activeSection = $state('summary');
|
||||
|
||||
// Extract data from load function
|
||||
const {
|
||||
databaseRows, summaryMain, restoredCount, notRestoredCount,
|
||||
databaseRowsAZ, summaryAZ, backupFiles, clientsData,
|
||||
alertsData, basesDeDatosList,
|
||||
errors // Capturar errores del servidor
|
||||
} = data;
|
||||
|
||||
// Derived Metrics
|
||||
const totalDatabases = summaryMain?.total_databases || 0;
|
||||
const totalSizeGB = summaryMain?.total_size_gb ? parseFloat(summaryMain.total_size_gb).toFixed(2) : '0';
|
||||
|
||||
// Disk space placeholders (passed from server or calculated)
|
||||
const diskC = "Free"; // Implement proper disk check
|
||||
const diskD = "Free";
|
||||
|
||||
onMount(() => {
|
||||
// Initialize DataTables
|
||||
// Note: In Svelte, we typically avoid jQuery, but for migration compatibility
|
||||
// with the specific glass theme and plugins, we initialize them here.
|
||||
// Ensure scripts in app.html are loaded.
|
||||
|
||||
// Use a timeout to ensure DOM is ready and scripts are loaded
|
||||
setTimeout(() => {
|
||||
if (typeof window.jQuery !== 'undefined') {
|
||||
const jq = window.jQuery;
|
||||
|
||||
// Initialize tables only if data exists
|
||||
if (databaseRows && databaseRows.length > 0) jq('#combinedTable').DataTable({ responsive: true });
|
||||
if (databaseRowsAZ && databaseRowsAZ.length > 0) jq('#combinedTable1').DataTable({ responsive: true });
|
||||
if (backupFiles && backupFiles.length > 0) jq('#backupTable').DataTable({ responsive: true });
|
||||
if (clientsData && clientsData.length > 0) jq('#clientTable').DataTable({ responsive: true });
|
||||
if (alertsData && alertsData.length > 0) jq('#alertsTable').DataTable({ responsive: true });
|
||||
if (basesDeDatosList && basesDeDatosList.length > 0) jq('#basesDeDatosTable').DataTable({ responsive: true });
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div class="d-flex min-vh-100">
|
||||
<Navbar />
|
||||
<Sidebar bind:activeSection />
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="md-content-wrapper">
|
||||
<!-- Header Section -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-5" data-aos="fade-down">
|
||||
<div>
|
||||
<h1 class="page-title">Panel de Control</h1>
|
||||
<p class="text-muted fs-5 mb-0">Sistema de Gestión de Bases de Datos</p>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="text-muted small">{new Date().toLocaleDateString('es-ES')}</div>
|
||||
<div class="fw-bold" id="current-time">{new Date().toLocaleTimeString('es-ES')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Global Alert Area -->
|
||||
{#if errors}
|
||||
{#if errors.primary}
|
||||
<div class="alert alert-danger d-flex align-items-center mb-4 shadow-sm" role="alert">
|
||||
<span class="material-icons-outlined me-2">error</span>
|
||||
<div>
|
||||
<strong>Error Servidor Principal:</strong> {errors.primary}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if errors.secondary}
|
||||
<div class="alert alert-warning d-flex align-items-center mb-4 shadow-sm" role="alert">
|
||||
<span class="material-icons-outlined me-2">warning</span>
|
||||
<div>
|
||||
<strong>Advertencia Servidor Secundario:</strong> {errors.secondary}
|
||||
<br><small>Algunos datos de clientes o catálogos podrían no estar disponibles.</small>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if errors.azure}
|
||||
<div class="alert alert-info d-flex align-items-center mb-4 shadow-sm" role="alert">
|
||||
<span class="material-icons-outlined me-2">cloud_off</span>
|
||||
<div>
|
||||
<strong>Aviso Azure:</strong> {errors.azure}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if errors.backups}
|
||||
<div class="alert alert-secondary d-flex align-items-center mb-4 shadow-sm" role="alert">
|
||||
<span class="material-icons-outlined me-2">folder_off</span>
|
||||
<div>
|
||||
<strong>Sistema de Archivos:</strong> {errors.backups}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if activeSection === 'summary'}
|
||||
<!-- Summary Section -->
|
||||
<section id="summarySection">
|
||||
<!-- Metrics -->
|
||||
<div class="row g-4 mb-5">
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="md-metric-card">
|
||||
<div class="material-icons-outlined md-metric-icon">storage</div>
|
||||
<div class="md-metric-value">{totalDatabases}</div>
|
||||
<div class="md-metric-label">Bases de Datos Totales</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- More metrics... simplified for brevity, assume similar structure -->
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="md-metric-card">
|
||||
<div class="material-icons-outlined md-metric-icon" style="color: var(--md-success)">cloud_done</div>
|
||||
<div class="md-metric-value" style="color: var(--md-success)">{totalSizeGB} GB</div>
|
||||
<div class="md-metric-label">Almacenamiento Utilizado</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="md-metric-card">
|
||||
<div class="material-icons-outlined md-metric-icon" style="color: var(--md-warning)">restore</div>
|
||||
<div class="md-metric-value" style="color: var(--md-warning)">{notRestoredCount}</div>
|
||||
<div class="md-metric-label">Pendientes de Restaurar</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="md-metric-card">
|
||||
<div class="material-icons-outlined md-metric-icon" style="color: var(--md-info)">check_circle</div>
|
||||
<div class="md-metric-value" style="color: var(--md-info)">{restoredCount}</div>
|
||||
<div class="md-metric-label">Restauradas 24h</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="md-card">
|
||||
<h4 class="md-card-title mb-4">Estado de Bases de Datos Local</h4>
|
||||
<div class="table-responsive">
|
||||
<table id="combinedTable" class="table display responsive nowrap" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Cliente</th>
|
||||
<th>Original</th>
|
||||
<th>Tamaño</th>
|
||||
<th>Restauración</th>
|
||||
<th>Estado</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each (databaseRows || []) as row}
|
||||
<tr>
|
||||
<td>{row.visible_name}</td>
|
||||
<td>{row.original_name}</td>
|
||||
<td>{row.size_mb ? Number(row.size_mb).toFixed(2) : 0} MB</td>
|
||||
<td>{row.last_restore_date ? new Date(row.last_restore_date).toLocaleDateString() : 'N/A'}</td>
|
||||
<td>
|
||||
{#if row.last_restore_date && new Date(row.last_restore_date) > new Date(Date.now() - 86400000)}
|
||||
<span class="badge bg-success">Actualizada</span>
|
||||
{:else}
|
||||
<span class="badge bg-danger">Atención</span>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted py-4">
|
||||
No hay datos disponibles o error de conexión.
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if activeSection === 'azure'}
|
||||
<!-- Azure Section -->
|
||||
<section id="AzureSection">
|
||||
<div class="md-card">
|
||||
<h4 class="md-card-title mb-4">Azure Cloud Databases</h4>
|
||||
<p class="text-muted mb-4">Instancias alojadas en servicios de nube.</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="combinedTable1" class="table w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nombre</th>
|
||||
<th>Nombre Original</th>
|
||||
<th>Tamaño</th>
|
||||
<th>Última Sync</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each (databaseRowsAZ || []) as row}
|
||||
<tr>
|
||||
<td>{row.visible_name}</td>
|
||||
<td>{row.original_name}</td>
|
||||
<td>{row.size_mb ? Number(row.size_mb).toFixed(2) : 0} MB</td>
|
||||
<td>{row.last_restore_date ? new Date(row.last_restore_date).toLocaleDateString() : 'N/A'}</td>
|
||||
</tr>
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted py-4">
|
||||
No hay datos de Azure disponibles.
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if activeSection === 'backups'}
|
||||
<section id="backupSection">
|
||||
<div class="md-card">
|
||||
<h4 class="md-card-title mb-4">Respaldos Almacenados</h4>
|
||||
<p class="text-muted mb-4">Archivos físicos encontrados en la ruta de respaldo.</p>
|
||||
|
||||
<table id="backupTable" class="table w-100">
|
||||
<thead><tr><th>Archivo</th><th>Cliente</th><th>Fecha</th><th>Tamaño</th></tr></thead>
|
||||
<tbody>
|
||||
{#each (backupFiles || []) as file}
|
||||
<tr>
|
||||
<td>{file.name}</td>
|
||||
<td>{file.client_name}</td>
|
||||
<td>{new Date(file.date).toLocaleString()}</td>
|
||||
<td>{file.size}</td>
|
||||
</tr>
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted py-4">
|
||||
No se encontraron archivos de respaldo o no hay acceso al disco.
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if activeSection === 'clients'}
|
||||
<section id="clientsSection">
|
||||
<div class="md-card">
|
||||
<h4 class="md-card-title mb-4">Catálogo de Clientes</h4>
|
||||
<table id="clientTable" class="table w-100">
|
||||
<thead><tr><th>ID</th><th>Nombre</th><th>Email</th><th>Estado</th></tr></thead>
|
||||
<tbody>
|
||||
{#each (clientsData || []) as client}
|
||||
<tr>
|
||||
<td>{client.ID}</td>
|
||||
<td>{client.Nombre}</td>
|
||||
<td>{client.CorreoNotificacion}</td>
|
||||
<td>
|
||||
{#if client.Activo}
|
||||
<span class="badge bg-success">Activo</span>
|
||||
{:else}
|
||||
<span class="badge bg-secondary">Inactivo</span>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted py-4">
|
||||
No hay datos de clientes (Verifique conexión secundaria).
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if activeSection === 'alerts'}
|
||||
<section id="alertsSection">
|
||||
<div class="md-card">
|
||||
<h4 class="md-card-title mb-4">Alertas Críticas</h4>
|
||||
<div class="alert alert-warning mb-4">
|
||||
<span class="material-icons-outlined align-middle fs-5 me-2">info</span>
|
||||
Mostrando bases de datos que no se han restaurado en los últimos 2 días.
|
||||
</div>
|
||||
|
||||
<table id="alertsTable" class="table w-100">
|
||||
<thead><tr><th>Base de Datos</th><th>Última Restauración</th><th>Severidad</th></tr></thead>
|
||||
<tbody>
|
||||
{#each (alertsData || []) as alert}
|
||||
<tr>
|
||||
<td>{alert.visible_name}</td>
|
||||
<td>{alert.last_restore_date ? new Date(alert.last_restore_date).toLocaleString() : 'Nunca'}</td>
|
||||
<td><span class="badge bg-danger">Crítica</span></td>
|
||||
</tr>
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="3" class="text-center text-muted py-4">
|
||||
¡Excelente! No hay alertas críticas pendientes.
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if activeSection === 'databases'}
|
||||
<section id="databasesSection">
|
||||
<div class="md-card">
|
||||
<h4 class="md-card-title mb-4">Gestión de Bases de Datos</h4>
|
||||
<table id="basesDeDatosTable" class="table w-100">
|
||||
<thead><tr><th>ID</th><th>Nodo</th><th>Nombre</th><th>Servidor</th></tr></thead>
|
||||
<tbody>
|
||||
{#each (basesDeDatosList || []) as bd}
|
||||
<tr>
|
||||
<td>{bd.ID}</td>
|
||||
<td>{bd.NodoSubNodo}</td>
|
||||
<td>{bd.Nombre}</td>
|
||||
<td>{bd.ServerName}</td>
|
||||
</tr>
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted py-4">
|
||||
No hay registros de gestión disponibles.
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user