cambios importates
This commit is contained in:
@@ -202,6 +202,194 @@ try {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ KPI: Actividad de Pacientes ══════════════════════════════ -->
|
||||
<div class="row mt-4" id="kpi-actividad-section">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white d-flex flex-wrap align-items-center gap-2 justify-content-between">
|
||||
<h5 class="mb-0"><i class="fas fa-chart-bar text-primary me-2"></i>Actividad de Pacientes — KPI</h5>
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
<!-- Rango rápido -->
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<button class="btn btn-outline-secondary kpi-quick" data-days="7">7 días</button>
|
||||
<button class="btn btn-secondary kpi-quick active" data-days="30">30 días</button>
|
||||
<button class="btn btn-outline-secondary kpi-quick" data-days="90">90 días</button>
|
||||
</div>
|
||||
<!-- Rango personalizado -->
|
||||
<input type="date" id="kpi-desde" class="form-control form-control-sm" style="width:140px">
|
||||
<span class="text-muted small">–</span>
|
||||
<input type="date" id="kpi-hasta" class="form-control form-control-sm" style="width:140px">
|
||||
<button class="btn btn-primary btn-sm" onclick="cargarKPI()">
|
||||
<i class="fas fa-filter me-1"></i>Filtrar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Tarjetas de totales -->
|
||||
<div class="row g-3 mb-4" id="kpi-cards">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card border-0 bg-primary bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-primary" id="kpi-total-usuarios">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-users me-1"></i>Usuarios únicos</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card border-0 bg-success bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-success" id="kpi-total-mensajes">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fab fa-whatsapp me-1"></i>Mensajes recibidos</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card border-0 bg-warning bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-warning" id="kpi-total-agend">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-calendar-check me-1"></i>Agendamientos</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card border-0 bg-info bg-opacity-10 text-center py-3">
|
||||
<div class="fs-4 fw-bold text-info" id="kpi-pico-dia">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-trophy me-1"></i>Día más activo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gráficas -->
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-8">
|
||||
<div class="card border-0 bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted mb-3"><i class="fas fa-calendar-alt me-1"></i>Por día</h6>
|
||||
<div style="position:relative; height:220px">
|
||||
<canvas id="kpiChartDia"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted mb-3"><i class="fas fa-calendar-week me-1"></i>Por día de la semana</h6>
|
||||
<div style="position:relative; height:220px">
|
||||
<canvas id="kpiChartSemana"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- ══ KPI: Tiempo de Respuesta de Asesores ════════════════════ -->
|
||||
<div class="row mt-4" id="kpi-asesor-section">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white d-flex flex-wrap align-items-center gap-2 justify-content-between">
|
||||
<h5 class="mb-0"><i class="fas fa-stopwatch text-danger me-2"></i>Tiempo de Respuesta de Asesores</h5>
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<button class="btn btn-outline-secondary kpi-asesor-quick" data-days="7">7 días</button>
|
||||
<button class="btn btn-secondary kpi-asesor-quick active" data-days="30">30 días</button>
|
||||
<button class="btn btn-outline-secondary kpi-asesor-quick" data-days="90">90 días</button>
|
||||
</div>
|
||||
<input type="date" id="kpia-desde" class="form-control form-control-sm" style="width:140px">
|
||||
<span class="text-muted small">–</span>
|
||||
<input type="date" id="kpia-hasta" class="form-control form-control-sm" style="width:140px">
|
||||
<button class="btn btn-danger btn-sm" onclick="cargarKPIAsesor()">
|
||||
<i class="fas fa-filter me-1"></i>Filtrar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Tarjetas de totales -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-2">
|
||||
<div class="card border-0 bg-danger bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-danger" id="kpia-total-sesiones">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-headset me-1"></i>Atenciones</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<div class="card border-0 bg-warning bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-warning" id="kpia-prom-resp">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-clock me-1"></i>Prom. 1ª respuesta</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<div class="card border-0 bg-info bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-info" id="kpia-prom-dur">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-hourglass-half me-1"></i>Prom. duración</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<div class="card border-0 bg-success bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-success" id="kpia-pct-rapidas">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-bolt me-1"></i>Resp. < 3 min</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<div class="card border-0 bg-secondary bg-opacity-10 text-center py-3">
|
||||
<div class="fs-2 fw-bold text-secondary" id="kpia-sin-resp">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-comment-slash me-1"></i>Sin respuesta</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gráficas -->
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-lg-8">
|
||||
<div class="card border-0 bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted mb-3"><i class="fas fa-chart-line me-1"></i>Promedio de respuesta por día (minutos)</h6>
|
||||
<div style="position:relative; height:200px">
|
||||
<canvas id="kpiaChartDia"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted mb-3"><i class="fas fa-chart-pie me-1"></i>Distribución de tiempos</h6>
|
||||
<div style="position:relative; height:200px">
|
||||
<canvas id="kpiaChartHist"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabla por asesor -->
|
||||
<div class="card border-0 bg-light">
|
||||
<div class="card-body p-0">
|
||||
<h6 class="text-muted mb-0 px-3 pt-3"><i class="fas fa-user-tie me-1"></i>Detalle por asesor</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0 mt-2" id="kpia-tabla-asesores">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Asesor</th>
|
||||
<th class="text-center">Atenciones</th>
|
||||
<th class="text-center">Prom. 1ª respuesta</th>
|
||||
<th class="text-center">Prom. duración</th>
|
||||
<th>Velocidad</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="kpia-body-asesores">
|
||||
<tr><td colspan="5" class="text-center text-muted py-3"><i class="fas fa-spinner fa-spin"></i></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Rate Limiting Monitor -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
@@ -2312,7 +2500,320 @@ try {
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<!-- ── / Historial Envíos Masivos JS ─────────────────────────────────── -->
|
||||
<!-- ── KPI Actividad de Pacientes JS ────────────────────────────────── -->
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
let chartDia = null;
|
||||
let chartSemana = null;
|
||||
|
||||
const fmt = n => Number(n).toLocaleString('es-CO');
|
||||
|
||||
// ── Inicializar fechas en los inputs ──────────────────────────────
|
||||
function initFechas(days = 30) {
|
||||
const hoy = new Date();
|
||||
const desde = new Date(hoy); desde.setDate(hoy.getDate() - (days - 1));
|
||||
document.getElementById('kpi-hasta').value = hoy.toISOString().slice(0, 10);
|
||||
document.getElementById('kpi-desde').value = desde.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
// ── Carga y renderiza ─────────────────────────────────────────────
|
||||
window.cargarKPI = async function () {
|
||||
const desde = document.getElementById('kpi-desde').value;
|
||||
const hasta = document.getElementById('kpi-hasta').value;
|
||||
if (!desde || !hasta) return;
|
||||
|
||||
try {
|
||||
const r = await fetch(`api/kpi_actividad.php?desde=${desde}&hasta=${hasta}`);
|
||||
const d = await r.json();
|
||||
if (!d.ok) throw new Error(d.error || 'Error');
|
||||
|
||||
// Tarjetas
|
||||
document.getElementById('kpi-total-usuarios').textContent = fmt(d.totales.usuarios);
|
||||
document.getElementById('kpi-total-mensajes').textContent = fmt(d.totales.mensajes);
|
||||
document.getElementById('kpi-total-agend').textContent = fmt(d.totales.agendamientos);
|
||||
document.getElementById('kpi-pico-dia').textContent = d.totales.pico_semana;
|
||||
|
||||
// Gráfica por día
|
||||
const labsDia = d.por_dia.map(x => {
|
||||
const f = new Date(x.fecha + 'T00:00:00');
|
||||
return f.toLocaleDateString('es-CO', { day:'2-digit', month:'short' });
|
||||
});
|
||||
const dataUsuarios = d.por_dia.map(x => x.usuarios);
|
||||
const dataAgend = d.por_dia.map(x => x.agendamientos);
|
||||
|
||||
if (chartDia) chartDia.destroy();
|
||||
chartDia = new Chart(document.getElementById('kpiChartDia'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labsDia,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Usuarios que escriben',
|
||||
data: dataUsuarios,
|
||||
backgroundColor: 'rgba(13,110,253,0.6)',
|
||||
borderRadius: 4,
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
label: 'Agendamientos',
|
||||
data: dataAgend,
|
||||
backgroundColor: 'rgba(255,193,7,0.75)',
|
||||
borderRadius: 4,
|
||||
order: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { position: 'top' } },
|
||||
scales: {
|
||||
x: { ticks: { maxRotation: 45, font: { size: 10 } } },
|
||||
y: { beginAtZero: true, ticks: { precision: 0 } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Gráfica por día de semana
|
||||
const labsSem = d.por_dia_semana.map(x => x.dia);
|
||||
const dataSemU = d.por_dia_semana.map(x => x.usuarios);
|
||||
const dataSemA = d.por_dia_semana.map(x => x.agendamientos);
|
||||
|
||||
if (chartSemana) chartSemana.destroy();
|
||||
chartSemana = new Chart(document.getElementById('kpiChartSemana'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labsSem,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Usuarios',
|
||||
data: dataSemU,
|
||||
backgroundColor: 'rgba(13,110,253,0.6)',
|
||||
borderRadius: 4,
|
||||
},
|
||||
{
|
||||
label: 'Agendamientos',
|
||||
data: dataSemA,
|
||||
backgroundColor: 'rgba(255,193,7,0.75)',
|
||||
borderRadius: 4,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { position: 'top' } },
|
||||
scales: {
|
||||
y: { beginAtZero: true, ticks: { precision: 0 } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error('KPI error:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Botones de rango rápido ───────────────────────────────────────
|
||||
document.querySelectorAll('.kpi-quick').forEach(btn => {
|
||||
btn.addEventListener('click', function () {
|
||||
document.querySelectorAll('.kpi-quick').forEach(b => {
|
||||
b.classList.remove('btn-secondary', 'active');
|
||||
b.classList.add('btn-outline-secondary');
|
||||
});
|
||||
this.classList.remove('btn-outline-secondary');
|
||||
this.classList.add('btn-secondary', 'active');
|
||||
initFechas(parseInt(this.dataset.days, 10));
|
||||
cargarKPI();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Carga inicial (30 días por defecto) ───────────────────────────
|
||||
// Solo se ejecuta cuando la pestaña dashboard está visible
|
||||
function kpiInit() {
|
||||
initFechas(30);
|
||||
cargarKPI();
|
||||
}
|
||||
|
||||
// Esperar a que el tab dashboard esté activo
|
||||
const observer = new MutationObserver(() => {
|
||||
const sec = document.getElementById('kpi-actividad-section');
|
||||
if (sec && sec.closest('.tab-content')?.style.display !== 'none') {
|
||||
kpiInit();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// Si la sección ya es visible al cargar, inicializar de inmediato
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const sec = document.getElementById('kpi-actividad-section');
|
||||
if (sec) kpiInit();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<!-- ── KPI Tiempo de Respuesta Asesores JS ──────────────────────────── -->
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
let chartDiaA = null;
|
||||
let chartHistA = null;
|
||||
|
||||
const fmtMin = m => m === null ? '—' : (m < 60 ? m + ' min' : Math.floor(m/60) + 'h ' + (m%60) + 'm');
|
||||
const escA = s => String(s||'').replace(/[<>&"]/g, c => ({'<':'<','>':'>','&':'&','"':'"'}[c]));
|
||||
|
||||
function initFechasA(days = 30) {
|
||||
const hoy = new Date();
|
||||
const desde = new Date(hoy); desde.setDate(hoy.getDate() - (days - 1));
|
||||
document.getElementById('kpia-hasta').value = hoy.toISOString().slice(0, 10);
|
||||
document.getElementById('kpia-desde').value = desde.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function velocidadBadge(min) {
|
||||
if (min === null) return '<span class="badge bg-secondary">Sin datos</span>';
|
||||
if (min < 3) return '<span class="badge bg-success">Muy rápido</span>';
|
||||
if (min < 10) return '<span class="badge bg-primary">Normal</span>';
|
||||
if (min < 20) return '<span class="badge bg-warning text-dark">Lento</span>';
|
||||
return '<span class="badge bg-danger">Muy lento</span>';
|
||||
}
|
||||
|
||||
window.cargarKPIAsesor = async function () {
|
||||
const desde = document.getElementById('kpia-desde').value;
|
||||
const hasta = document.getElementById('kpia-hasta').value;
|
||||
if (!desde || !hasta) return;
|
||||
|
||||
try {
|
||||
const r = await fetch(`api/kpi_respuesta_asesor.php?desde=${desde}&hasta=${hasta}`);
|
||||
const d = await r.json();
|
||||
if (!d.ok) throw new Error(d.error || 'Error');
|
||||
|
||||
const t = d.totales;
|
||||
document.getElementById('kpia-total-sesiones').textContent = t.sesiones;
|
||||
document.getElementById('kpia-prom-resp').textContent = fmtMin(t.prom_respuesta);
|
||||
document.getElementById('kpia-prom-dur').textContent = fmtMin(t.prom_duracion);
|
||||
document.getElementById('kpia-pct-rapidas').textContent = t.pct_rapidas + '%';
|
||||
document.getElementById('kpia-sin-resp').textContent = t.sin_respuesta;
|
||||
|
||||
// Gráfica por día
|
||||
const labsDia = d.serie_dia.map(x => {
|
||||
const f = new Date(x.fecha + 'T00:00:00');
|
||||
return f.toLocaleDateString('es-CO', { day:'2-digit', month:'short' });
|
||||
});
|
||||
const dataResp = d.serie_dia.map(x => x.prom_respuesta);
|
||||
const dataSes = d.serie_dia.map(x => x.sesiones);
|
||||
|
||||
if (chartDiaA) chartDiaA.destroy();
|
||||
chartDiaA = new Chart(document.getElementById('kpiaChartDia'), {
|
||||
data: {
|
||||
labels: labsDia,
|
||||
datasets: [
|
||||
{
|
||||
type: 'line',
|
||||
label: 'Prom. respuesta (min)',
|
||||
data: dataResp,
|
||||
borderColor: 'rgba(220,53,69,0.9)',
|
||||
backgroundColor: 'rgba(220,53,69,0.1)',
|
||||
fill: true,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
yAxisID: 'y',
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
label: 'Atenciones',
|
||||
data: dataSes,
|
||||
backgroundColor: 'rgba(108,117,125,0.3)',
|
||||
borderRadius: 3,
|
||||
yAxisID: 'y2',
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { position: 'top' } },
|
||||
scales: {
|
||||
x: { ticks: { maxRotation: 45, font: { size: 10 } } },
|
||||
y: { beginAtZero: true, ticks: { precision: 0 }, title: { display: true, text: 'Min' } },
|
||||
y2: { beginAtZero: true, position: 'right', grid: { drawOnChartArea: false }, ticks: { precision: 0 }, title: { display: true, text: 'Atenciones' } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Histograma de distribución
|
||||
const labsH = d.histograma.map(x => x.rango);
|
||||
const dataH = d.histograma.map(x => x.sesiones);
|
||||
const colores = [
|
||||
'rgba(25,135,84,0.7)', // < 1 min verde
|
||||
'rgba(13,110,253,0.7)', // 1-3 min azul
|
||||
'rgba(255,193,7,0.7)', // 3-5 min amarillo
|
||||
'rgba(253,126,20,0.7)', // 5-10 min naranja
|
||||
'rgba(220,53,69,0.7)', // 10-20 rojo
|
||||
'rgba(108,17,17,0.7)', // >20 rojo oscuro
|
||||
'rgba(108,117,125,0.7)', // Sin resp gris
|
||||
];
|
||||
|
||||
if (chartHistA) chartHistA.destroy();
|
||||
chartHistA = new Chart(document.getElementById('kpiaChartHist'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: labsH,
|
||||
datasets: [{ data: dataH, backgroundColor: colores, borderWidth: 1 }],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'right', labels: { font: { size: 11 } } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Tabla de asesores
|
||||
const tbody = document.getElementById('kpia-body-asesores');
|
||||
if (!d.asesores.length) {
|
||||
tbody.innerHTML = '<tr><td colspan="5" class="text-center text-muted py-2">Sin datos</td></tr>';
|
||||
} else {
|
||||
tbody.innerHTML = d.asesores.map(a => `
|
||||
<tr>
|
||||
<td class="fw-semibold">${escA(a.operador)}</td>
|
||||
<td class="text-center">${a.sesiones}</td>
|
||||
<td class="text-center">${fmtMin(a.prom_respuesta)}</td>
|
||||
<td class="text-center">${fmtMin(a.prom_duracion)}</td>
|
||||
<td>${velocidadBadge(a.prom_respuesta)}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('KPI Asesores error:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Botones rápidos
|
||||
document.querySelectorAll('.kpi-asesor-quick').forEach(btn => {
|
||||
btn.addEventListener('click', function () {
|
||||
document.querySelectorAll('.kpi-asesor-quick').forEach(b => {
|
||||
b.classList.remove('btn-secondary', 'active');
|
||||
b.classList.add('btn-outline-secondary');
|
||||
});
|
||||
this.classList.remove('btn-outline-secondary');
|
||||
this.classList.add('btn-secondary', 'active');
|
||||
initFechasA(parseInt(this.dataset.days, 10));
|
||||
cargarKPIAsesor();
|
||||
});
|
||||
});
|
||||
|
||||
// Carga inicial
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const sec = document.getElementById('kpi-asesor-section');
|
||||
if (sec) { initFechasA(30); cargarKPIAsesor(); }
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<!-- ── / KPI Tiempo de Respuesta Asesores JS ─────────────────────────── -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user