This commit is contained in:
kvilches1410
2026-04-07 10:51:27 -03:00
parent d7f7339fde
commit 72050df789
3 changed files with 74 additions and 15 deletions
+46 -9
View File
@@ -231,27 +231,64 @@
<!-- KPIs del período -->
<h3 style="font-size:14px; margin-bottom:12px; color:#172b4d;">Métricas del período seleccionado</h3>
<div class="kpi-row">
<div class="kpi-row-3" style="margin-bottom:16px;">
<div class="kpi qa" title="Total de tickets que han pasado por QA">
<div class="kpi-value" id="kpi-qa-tickets">—</div>
<div class="kpi-label">Tickets QA</div>
<div class="kpi-sub">Procesados en el período</div>
</div>
<div class="kpi qa-time" title="Horas dedicadas a revisar un ticket (Tiempo neto en la columna QA)">
<div class="kpi-value" id="kpi-qa-avg">—</div>
<div class="kpi-label">Tiempo Prom. QA</div>
<div class="kpi-sub">Horas promedio por ticket</div>
<div class="kpi qa-time">
<div class="kpi-value" id="kpi-qa-p50">—</div>
<div class="kpi-label">
<div class="pop-trigger" style="border-bottom: 1px dotted #5e6c84;">Tiempo P50 QA
<div class="pop-content" style="text-transform:none; font-weight:normal; letter-spacing:normal; text-align:left;">
<div class="pop-item" style="font-size:12px;"><strong>Percentil 50 (Mediana)</strong><br>El 50% de las pruebas se completan en esta cantidad de horas netas o menos.</div>
</div>
<div class="kpi qa-queue" title="Tiempo pasivo promedio esperando ser tomado (Cola 'Ready to QA')">
<div class="kpi-value" id="kpi-qa-rtqa">—</div>
<div class="kpi-label">Tiempo Prom. en Cola</div>
<div class="kpi-sub">Horas esperando READY TO QA</div>
</div>
</div>
<div class="kpi-sub">Horas en prueba</div>
</div>
<div class="kpi qa-time" style="border-color:#ff8b00;">
<div class="kpi-value" id="kpi-qa-p90">—</div>
<div class="kpi-label">
<div class="pop-trigger" style="border-bottom: 1px dotted #5e6c84;">Tiempos P90 y P99 QA
<div class="pop-content" style="text-transform:none; font-weight:normal; letter-spacing:normal; text-align:left;">
<div class="pop-item" style="font-size:12px;"><strong>Cuellos de Botella QA</strong><br><strong>P90:</strong> El 90% logran probarse en este tiempo.<br><strong>P99:</strong> El 99% logra terminar en este tiempo.</div>
</div>
</div>
</div>
<div class="kpi-sub" id="kpi-qa-p99-sub">P99: — hs</div>
</div>
</div>
<div class="kpi-row-3">
<div class="kpi returned" title="Tickets que rebotaron en QA y el desarrollador tuvo que retomar ('Reaperturas' o regresos al ciclo En Curso)">
<div class="kpi-value" id="kpi-qa-returned">—</div>
<div class="kpi-label">Tickets Devueltos</div>
<div class="kpi-sub" id="kpi-qa-returned-pct">Volvieron a En Curso</div>
</div>
<div class="kpi qa-queue">
<div class="kpi-value" id="kpi-qa-rtqa-p50">—</div>
<div class="kpi-label">
<div class="pop-trigger" style="border-bottom: 1px dotted #5e6c84;">Tiempo P50 RTQA
<div class="pop-content" style="text-transform:none; font-weight:normal; letter-spacing:normal; text-align:left;">
<div class="pop-item" style="font-size:12px;"><strong>Percentil 50 (Mediana)</strong><br>El 50% de los tickets espera esta cantidad de horas (o menos) en la cola antes de ser tomado por QA.</div>
</div>
</div>
</div>
<div class="kpi-sub">Horas en cola</div>
</div>
<div class="kpi qa-queue" style="border-color:#ff8b00;">
<div class="kpi-value" id="kpi-qa-rtqa-p90">—</div>
<div class="kpi-label">
<div class="pop-trigger" style="border-bottom: 1px dotted #5e6c84;">Tiempos P90 y P99 Cola
<div class="pop-content" style="text-transform:none; font-weight:normal; letter-spacing:normal; text-align:left;">
<div class="pop-item" style="font-size:12px;"><strong>Cuellos de Botella RTQA</strong><br>Casos atascados sin tester asignado.<br><br><strong>P90:</strong> El 90% de los tickets sale de la cola en este tiempo.<br><strong>P99:</strong> Bloqueos máximos en la cola.</div>
</div>
</div>
</div>
<div class="kpi-sub" id="kpi-qa-rtqa-p99-sub">P99: — hs</div>
</div>
</div>
<!-- Gráficos fila 1 -->
+8 -2
View File
@@ -357,10 +357,16 @@ function renderQASection() {
// ── KPIs DEL PERÍODO ──────────────────────────────────────────────────────
document.getElementById('kpi-qa-tickets').textContent = qa.tickets ?? '—';
document.getElementById('kpi-qa-avg').textContent = qa.avgTimeQa != null ? qa.avgTimeQa + 'hs' : '—';
document.getElementById('kpi-qa-rtqa').textContent = qa.avgTimeRtqa != null ? qa.avgTimeRtqa + 'hs' : '—';
document.getElementById('kpi-qa-returned').textContent = qa.ticketsReturned ?? '—';
document.getElementById('kpi-qa-p50').textContent = qa.p50Qa != null ? qa.p50Qa + 'hs' : '—';
document.getElementById('kpi-qa-p90').textContent = qa.p90Qa != null ? qa.p90Qa + 'hs' : '—';
document.getElementById('kpi-qa-p99-sub').textContent = qa.p99Qa != null ? 'P99: ' + qa.p99Qa + ' hs' : 'P99: — hs';
document.getElementById('kpi-qa-rtqa-p50').textContent = qa.p50Rtqa != null ? qa.p50Rtqa + 'hs' : '—';
document.getElementById('kpi-qa-rtqa-p90').textContent = qa.p90Rtqa != null ? qa.p90Rtqa + 'hs' : '—';
document.getElementById('kpi-qa-rtqa-p99-sub').textContent = qa.p99Rtqa != null ? 'P99: ' + qa.p99Rtqa + ' hs' : 'P99: — hs';
const pctRet = qa.tickets > 0 ? ((qa.ticketsReturned / qa.tickets) * 100).toFixed(1) : 0;
document.getElementById('kpi-qa-returned-pct').textContent =
`${pctRet}% del total pasó por QA más de una vez`;
+17 -1
View File
@@ -138,6 +138,7 @@ function processIssues(issues, period = '90d') {
const qaData = {
tickets: 0, ticketsReturned: 0, totalTimeQa: 0, totalTimeRtqa: 0,
monthlyTotalQa: {}, monthlyCountQa: {}, monthlyTotalRtqa: {}, monthlyCountRtqa: {}, issues: [],
timesQa: [], timesRtqa: []
};
const deliveryData = {
@@ -253,6 +254,7 @@ function processIssues(issues, period = '90d') {
if (timeQaMin > 0) {
qaData.tickets++;
qaData.totalTimeQa += timeQaMin;
qaData.timesQa.push(timeQaMin);
if (timesRet > 1) qaData.ticketsReturned++;
if (qaMonth) {
qaData.monthlyTotalQa[qaMonth] = (qaData.monthlyTotalQa[qaMonth] || 0) + timeQaMin;
@@ -264,11 +266,14 @@ function processIssues(issues, period = '90d') {
});
}
if (timeRtqaMin > 0 && qaMonth) {
if (timeRtqaMin > 0) {
qaData.timesRtqa.push(timeRtqaMin);
if (qaMonth) {
qaData.totalTimeRtqa += timeRtqaMin;
qaData.monthlyTotalRtqa[qaMonth] = (qaData.monthlyTotalRtqa[qaMonth] || 0) + timeRtqaMin;
qaData.monthlyCountRtqa[qaMonth] = (qaData.monthlyCountRtqa[qaMonth] || 0) + 1;
}
}
const timeCrMin = f[config.fields.crTotalTime] ?? 0;
if (timeCrMin > 0) {
@@ -341,6 +346,16 @@ function processIssues(issues, period = '90d') {
const p90Cr = minsToHrs(getPercentile(crTimesSorted, 0.9));
const p99Cr = minsToHrs(getPercentile(crTimesSorted, 0.99));
const qaTimesSorted = qaData.timesQa.sort((a, b) => a - b);
const p50Qa = minsToHrs(getPercentile(qaTimesSorted, 0.5));
const p90Qa = minsToHrs(getPercentile(qaTimesSorted, 0.9));
const p99Qa = minsToHrs(getPercentile(qaTimesSorted, 0.99));
const rtqaTimesSorted = qaData.timesRtqa.sort((a, b) => a - b);
const p50Rtqa = minsToHrs(getPercentile(rtqaTimesSorted, 0.5));
const p90Rtqa = minsToHrs(getPercentile(rtqaTimesSorted, 0.9));
const p99Rtqa = minsToHrs(getPercentile(rtqaTimesSorted, 0.99));
return {
lastUpdated: new Date().toISOString(),
assigneeData, monthlyDone, issuesList, warnings, currentStates,
@@ -357,6 +372,7 @@ function processIssues(issues, period = '90d') {
},
qaData: {
...qaData, monthlyAvgQa, monthlyAvgRtqa,
p50Qa, p90Qa, p99Qa, p50Rtqa, p90Rtqa, p99Rtqa,
avgTimeQa: qaData.tickets > 0 ? minsToHrs(qaData.totalTimeQa / qaData.tickets) : 0,
avgTimeRtqa: qaData.tickets > 0 ? minsToHrs(qaData.totalTimeRtqa / qaData.tickets) : 0
}