diff --git a/assets/css/style.css b/assets/css/style.css index b5c2c454..61bb57a9 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -528,6 +528,64 @@ h1, .h1 { height: 320px !important; } +.cc-kpi-status-legend { + border: 1px solid rgba(15, 23, 42, .08); + border-radius: 1rem; + padding: 1rem; + background: linear-gradient(180deg, rgba(248, 250, 252, .94), rgba(255, 255, 255, .98)); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .6); + display: flex; + flex-direction: column; + gap: .65rem; +} + +.cc-kpi-status-item { + display: flex; + align-items: center; + gap: .75rem; + padding: .75rem .85rem; + border-radius: .95rem; + border: 1px solid rgba(15, 23, 42, .06); + background: #fff; + box-shadow: 0 .55rem 1rem rgba(15, 23, 42, .04); +} + +.cc-kpi-status-dot { + width: .9rem; + height: .9rem; + border-radius: 999px; + flex: 0 0 .9rem; + border: 2px solid rgba(255, 255, 255, .9); + box-shadow: 0 0 0 1px rgba(15, 23, 42, .06); +} + +.cc-kpi-status-text { + min-width: 0; + flex: 1 1 auto; +} + +.cc-kpi-status-label { + font-size: .94rem; + font-weight: 700; + line-height: 1.1; + color: #111827; +} + +.cc-kpi-status-count { + font-size: .78rem; + color: #6c757d; + line-height: 1.1; + margin-top: .18rem; +} + +.cc-kpi-status-percent { + font-size: 1.05rem; + font-weight: 800; + line-height: 1; + color: #111827; + white-space: nowrap; +} + .cc-kpi-footnote { font-size: .85rem; color: #6c757d; @@ -543,6 +601,18 @@ h1, .h1 { min-height: 280px; height: 280px !important; } + + .cc-kpi-status-legend { + padding: .9rem; + } + + .cc-kpi-status-item { + padding: .7rem .75rem; + } + + .cc-kpi-status-percent { + font-size: 1rem; + } } @media (max-width: 575.98px) { diff --git a/call_center_pro.php b/call_center_pro.php index c85942d4..3df45489 100644 --- a/call_center_pro.php +++ b/call_center_pro.php @@ -479,15 +479,15 @@ try { ? ($selectedAssessorFilterLabel !== '' ? 'KPI diario · ' . $selectedAssessorFilterLabel : 'KPI diario por asesora') : 'Mi KPI diario'; $performanceSubtitle = $isAdmin - ? 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos de días anteriores confirmados hoy.' - : 'Tus KPIs de hoy se calculan sobre tus pedidos asignados. Confirmación y recuperación se separan para ver tu productividad real.'; + ? 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy.' + : 'Tus KPIs de hoy se calculan sobre tus pedidos asignados hoy. Confirmación = asignados hoy confirmados hoy. Recuperación = asignados antes de hoy confirmados hoy.'; $performanceDashboardHtml = cc_test_render_performance_dashboard($performanceDashboard, [ 'title' => $performanceTitle, 'subtitle' => $performanceSubtitle, - 'chart1_title' => $isAdmin ? 'Rendimiento por asesora hoy' : 'Mi rendimiento de hoy', - 'chart1_note' => 'Asignados, confirmados nuevos, recuperados y cancelados de hoy.', + 'chart1_title' => $isAdmin ? 'Distribución de estados' : 'Mi distribución de estados', + 'chart1_note' => 'Verde = confirmados de pedidos asignados hoy, amarillo = confirmados de pedidos asignados antes de hoy, azul = seguimiento abierto, gris = repetidos, rojo = cancelados. La lista lateral muestra el % de cada estado.', 'chart2_title' => $isAdmin ? 'Evolución de los últimos 7 días' : 'Tu evolución de los últimos 7 días', - 'chart2_note' => 'La línea de confirmados separa pedidos nuevos y recuperados.', + 'chart2_note' => 'La línea de confirmados separa pedidos asignados hoy y pedidos asignados antes de hoy.', 'footnote' => 'Seguimiento abierto = pedidos en SE ENVIO NUMERO DE CUENTA que todavía no pasan a Promo Final.', ]); diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index 646a7888..38da4acb 100644 --- a/gestiones_callcenter.php +++ b/gestiones_callcenter.php @@ -678,11 +678,11 @@ try { $performanceDashboard = cc_test_build_performance_dashboard($orders, $assessors, 7); $performanceDashboardHtml = cc_test_render_performance_dashboard($performanceDashboard, [ 'title' => 'KPI diario por asesora', - 'subtitle' => 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos de días anteriores confirmados hoy.', - 'chart1_title' => 'Rendimiento por asesora hoy', - 'chart1_note' => 'Asignados, confirmados nuevos, recuperados y cancelados de hoy.', + 'subtitle' => 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy.', + 'chart1_title' => 'Distribución de estados', + 'chart1_note' => 'Verde = confirmados de pedidos asignados hoy, amarillo = confirmados de pedidos asignados antes de hoy, azul = seguimiento abierto, gris = repetidos, rojo = cancelados. La lista lateral muestra el % de cada estado.', 'chart2_title' => 'Evolución de los últimos 7 días', - 'chart2_note' => 'La línea de confirmados separa pedidos nuevos y recuperados.', + 'chart2_note' => 'La línea de confirmados separa pedidos asignados hoy y pedidos asignados antes de hoy.', 'footnote' => 'Seguimiento abierto = pedidos en SE ENVIO NUMERO DE CUENTA que todavía no pasan a Promo Final.', ]); diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index c825bd9a..92c60908 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -725,6 +725,7 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in 'recovered' => 0, 'canceled' => 0, 'followup_open' => 0, + 'repeated' => 0, ]; } @@ -735,6 +736,7 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in 'confirmed_today_recovered' => 0, 'confirmed_today_total' => 0, 'canceled_today' => 0, + 'repeated_today' => 0, 'followup_open' => 0, 'confirm_rate_today' => 0.0, 'recovery_rate_today' => 0.0, @@ -760,6 +762,7 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in 'recovered' => 0, 'canceled' => 0, 'followup_open' => 0, + 'repeated' => 0, ]; } @@ -774,6 +777,7 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in $isPromoFinal = !empty($followupInfo['is_promo_final']) && !empty($order['promo_final_evidence_path']); $isConfirmed = in_array($estado, cc_test_confirmed_states(), true); $isCanceled = $estado === 'CANCELADO'; + $isRepeated = $estado === 'REPETIDO'; $isFollowupOpen = $estado === 'SE ENVIO NUMERO DE CUENTA' && !$isPromoFinal; if ($assignedKey === $todayKey) { @@ -790,8 +794,10 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in $summary['followup_open']++; } + $confirmedReferenceKey = $assignedKey ?? $originKey; + if ($isConfirmed && $actionKey === $todayKey) { - if ($originKey !== null && $originKey < $todayKey) { + if ($confirmedReferenceKey !== null && $confirmedReferenceKey < $todayKey) { $bucketStats[$bucketKey]['recovered']++; $summary['confirmed_today_recovered']++; } else { @@ -800,7 +806,7 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in } if (isset($trend[$actionKey])) { - if ($originKey !== null && $originKey < $actionKey) { + if ($confirmedReferenceKey !== null && $confirmedReferenceKey < $actionKey) { $trend[$actionKey]['recovered']++; } else { $trend[$actionKey]['confirmed_new']++; @@ -816,6 +822,11 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in $trend[$actionKey]['canceled']++; } } + + if ($isRepeated && $actionKey === $todayKey) { + $bucketStats[$bucketKey]['repeated']++; + $summary['repeated_today']++; + } } $summary['confirmed_today_total'] = $summary['confirmed_today_new'] + $summary['confirmed_today_recovered']; @@ -886,6 +897,37 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in return [ 'summary' => $summary, + 'status_chart' => [ + 'labels' => ['Confirmados nuevos', 'Recuperados', 'Seguimiento abierto', 'Repetidos', 'Cancelados'], + 'datasets' => [ + [ + 'label' => 'Estados', + 'data' => [ + (int) ($summary['confirmed_today_new'] ?? 0), + (int) ($summary['confirmed_today_recovered'] ?? 0), + (int) ($summary['followup_open'] ?? 0), + (int) ($summary['repeated_today'] ?? 0), + (int) ($summary['canceled_today'] ?? 0), + ], + 'backgroundColor' => [ + 'rgba(25, 135, 84, 0.9)', + 'rgba(255, 193, 7, 0.92)', + 'rgba(13, 110, 253, 0.88)', + 'rgba(108, 117, 125, 0.84)', + 'rgba(220, 53, 69, 0.9)', + ], + 'borderColor' => [ + 'rgba(25, 135, 84, 1)', + 'rgba(245, 158, 11, 1)', + 'rgba(13, 110, 253, 1)', + 'rgba(108, 117, 125, 1)', + 'rgba(220, 53, 69, 1)', + ], + 'borderWidth' => 2, + 'hoverOffset' => 6, + ], + ], + ], 'advisor_count' => $advisorCount, 'advisor_chart' => [ 'labels' => $advisorLabels, @@ -983,11 +1025,11 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in function cc_test_render_performance_dashboard(array $dashboard, array $options = []): string { $title = trim((string) ($options['title'] ?? 'KPI diario por asesora')); - $subtitle = trim((string) ($options['subtitle'] ?? 'Confirmación = pedidos de hoy cerrados hoy. Recuperación = pedidos de otros días cerrados hoy.')); - $chart1Title = trim((string) ($options['chart1_title'] ?? 'Rendimiento por asesora hoy')); - $chart1Note = trim((string) ($options['chart1_note'] ?? 'Asignados, confirmados nuevos, recuperados y cancelados de hoy.')); + $subtitle = trim((string) ($options['subtitle'] ?? 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy.')); + $chart1Title = trim((string) ($options['chart1_title'] ?? 'Distribución de estados')); + $chart1Note = trim((string) ($options['chart1_note'] ?? 'Verde = confirmados de pedidos asignados hoy, amarillo = confirmados de pedidos asignados antes de hoy, azul = seguimiento abierto, gris = repetidos, rojo = cancelados. La lista lateral muestra el % de cada estado.')); $chart2Title = trim((string) ($options['chart2_title'] ?? 'Evolución de los últimos 7 días')); - $chart2Note = trim((string) ($options['chart2_note'] ?? 'La línea de confirmados separa pedidos nuevos y recuperados.')); + $chart2Note = trim((string) ($options['chart2_note'] ?? 'La línea de confirmados separa pedidos asignados hoy y pedidos asignados antes de hoy.')); $footnote = trim((string) ($options['footnote'] ?? 'Seguimiento abierto = pedidos en SE ENVIO NUMERO DE CUENTA que todavía no pasan a Promo Final.')); $sectionId = preg_replace('/[^A-Za-z0-9_-]+/', '', (string) ($options['section_id'] ?? 'ccPerformanceDashboard')); if ($sectionId === '') { @@ -995,9 +1037,38 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options = } $summary = (array) ($dashboard['summary'] ?? []); - $advisorChart = (array) ($dashboard['advisor_chart'] ?? []); $trendChart = (array) ($dashboard['trend_chart'] ?? []); - $advisorCount = (int) ($dashboard['advisor_count'] ?? 0); + $statusTotal = (int) ($summary['confirmed_today_new'] ?? 0) + + (int) ($summary['confirmed_today_recovered'] ?? 0) + + (int) ($summary['followup_open'] ?? 0) + + (int) ($summary['repeated_today'] ?? 0) + + (int) ($summary['canceled_today'] ?? 0); + + $statusLegendItems = []; + $statusLegendRows = [ + ['label' => 'Confirmados nuevos', 'value' => (int) ($summary['confirmed_today_new'] ?? 0), 'color' => 'rgba(25, 135, 84, 0.9)'], + ['label' => 'Recuperados', 'value' => (int) ($summary['confirmed_today_recovered'] ?? 0), 'color' => 'rgba(255, 193, 7, 0.92)'], + ['label' => 'Seguimiento abierto', 'value' => (int) ($summary['followup_open'] ?? 0), 'color' => 'rgba(13, 110, 253, 0.88)'], + ['label' => 'Repetidos', 'value' => (int) ($summary['repeated_today'] ?? 0), 'color' => 'rgba(108, 117, 125, 0.84)'], + ['label' => 'Cancelados', 'value' => (int) ($summary['canceled_today'] ?? 0), 'color' => 'rgba(220, 53, 69, 0.9)'], + ]; + if ($statusTotal > 0) { + foreach ($statusLegendRows as $row) { + $statusLegendItems[] = [ + 'label' => $row['label'], + 'value' => (int) $row['value'], + 'percentage' => round(((float) $row['value'] / $statusTotal) * 100, 1), + 'color' => $row['color'], + ]; + } + } else { + $statusLegendItems[] = [ + 'label' => 'Sin estados', + 'value' => 0, + 'percentage' => 0.0, + 'color' => 'rgba(108, 117, 125, 0.65)', + ]; + } $badges = [ ['label' => 'Asignados hoy', 'value' => (int) ($summary['assigned_today'] ?? 0), 'class' => 'cc-kpi-pill-primary'], @@ -1016,6 +1087,9 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options = if (isset($summary['confirmed_today_recovered'])) { $extraLineParts[] = (int) $summary['confirmed_today_recovered'] . ' recuperados'; } + if (isset($summary['repeated_today'])) { + $extraLineParts[] = (int) $summary['repeated_today'] . ' repetidos'; + } if (isset($summary['canceled_today'])) { $extraLineParts[] = (int) $summary['canceled_today'] . ' cancelados'; } @@ -1057,10 +1131,29 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options =

- + -
- +
+
+
+ +
+
+
+
+
Detalle por estado (%)
+ +
+ +
+
+
pedidos
+
+
%
+
+ +
+
@@ -1092,51 +1185,104 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options =