Actividad: tarjeta de salud de la voz del televisor
La bitácora turnero_tv_log solo se podía consultar con SQL, así que en la práctica nadie la iba a mirar. La pantalla de Actividad (Reportes → Actividad) muestra ahora, en lenguaje llano: cuántos llamados con voz hubo en 7 días, qué porcentaje habló completo, cuántos salieron mudos y cuántos se recuperaron con reintento, más los últimos fallos con fecha, turno y voz usada. La tarjeta no aparece mientras no haya datos (recién desplegado), para no mostrar una sección vacía. De paso: el ancla del parche se comió el async de exportarCSV; restaurado y verificado bloque por bloque. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a28ae82402
commit
26efe10ae6
@@ -111,6 +111,18 @@ require_once __DIR__ . '/shared/components/sidebar.php';
|
||||
</div>
|
||||
|
||||
<!-- Tabla -->
|
||||
<!-- Salud de la voz del televisor: alimentada por turnero_tv_log.
|
||||
Responde de un vistazo «¿la pantalla está llamando bien?» sin
|
||||
tener que consultar la base a mano. -->
|
||||
<div class="card border-0 shadow-sm mb-3" id="card-voz-tv" style="display:none">
|
||||
<div class="card-body py-3">
|
||||
<h6 class="mb-2"><i class="fas fa-volume-up text-primary me-1"></i>
|
||||
Voz del televisor — últimos 7 días</h6>
|
||||
<div class="d-flex gap-4 flex-wrap mb-2" id="voz-tv-resumen"></div>
|
||||
<div id="voz-tv-incidentes" class="small"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
<div id="act-spinner" class="text-center py-5 text-muted">
|
||||
@@ -277,6 +289,52 @@ function verDetalle(i) {
|
||||
new bootstrap.Modal(document.getElementById('modalDetalle')).show();
|
||||
}
|
||||
|
||||
// ── Salud de la voz del televisor ────────────────────────────
|
||||
const VOZ_EVENTO_TXT = {
|
||||
mudo: ['🔇 No sonó', 'text-danger', 'El llamado salió sin voz: ni el reintento habló'],
|
||||
sin_start: ['⚠️ Arrancó con reintento','text-warning','La voz elegida no arrancó; habló la del navegador'],
|
||||
error: ['⚠️ Error del motor', 'text-warning', 'El motor de voz falló a mitad; se reintentó'],
|
||||
};
|
||||
|
||||
async function cargarVozTV() {
|
||||
try {
|
||||
const r = await fetch('api/lab/get_tv_log.php');
|
||||
const d = await r.json();
|
||||
if (!d.ok) return;
|
||||
// Sin datos todavía (recién desplegado): la tarjeta no aparece
|
||||
if (!d.anuncios_7d && !d.incidentes.length) return;
|
||||
|
||||
document.getElementById('card-voz-tv').style.display = '';
|
||||
const pct = d.anuncios_7d ? Math.round(d.completos_7d / d.anuncios_7d * 100) : 0;
|
||||
document.getElementById('voz-tv-resumen').innerHTML = `
|
||||
<div><span class="fw-bold fs-5">${d.anuncios_7d}</span>
|
||||
<span class="text-muted small">llamados con voz</span></div>
|
||||
<div><span class="fw-bold fs-5 ${pct >= 97 ? 'text-success' : 'text-warning'}">${pct}%</span>
|
||||
<span class="text-muted small">hablados completos</span></div>
|
||||
<div><span class="fw-bold fs-5 ${d.mudos_7d ? 'text-danger' : 'text-success'}">${d.mudos_7d}</span>
|
||||
<span class="text-muted small">salieron mudos</span></div>
|
||||
<div><span class="fw-bold fs-5 ${d.avisos_7d ? 'text-warning' : 'text-success'}">${d.avisos_7d}</span>
|
||||
<span class="text-muted small">se recuperaron con reintento</span></div>`;
|
||||
|
||||
const inc = document.getElementById('voz-tv-incidentes');
|
||||
if (!d.incidentes.length) {
|
||||
inc.innerHTML = '<span class="text-success"><i class="fas fa-check-circle me-1"></i>Sin fallos registrados</span>';
|
||||
return;
|
||||
}
|
||||
inc.innerHTML = '<div class="text-muted mb-1">Últimos fallos:</div>' +
|
||||
d.incidentes.slice(0, 10).map(i => {
|
||||
const [txt, cls, ayuda] = VOZ_EVENTO_TXT[i.evento] || [i.evento, '', ''];
|
||||
return `<div class="py-1 border-bottom" title="${ayuda}">
|
||||
<span class="text-muted">${(i.cuando || '').slice(0, 16)}</span>
|
||||
· turno <b>${i.turno || '—'}</b>
|
||||
· <span class="${cls}">${txt}</span>
|
||||
${i.voz ? `<span class="text-muted"> (voz: ${i.voz})</span>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
} catch (_) {}
|
||||
}
|
||||
cargarVozTV();
|
||||
|
||||
async function exportarCSV() {
|
||||
const params = new URLSearchParams({
|
||||
desde: document.getElementById('act-desde').value,
|
||||
|
||||
Reference in New Issue
Block a user