diff --git a/assets/css/style.css b/assets/css/style.css index 61bb57a9..2acd9193 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -512,6 +512,18 @@ h1, .h1 { background: linear-gradient(135deg, rgba(108, 117, 125, .12), rgba(108, 117, 125, .04)); } +.cc-kpi-pill-total { + background: linear-gradient(135deg, rgba(20, 184, 166, .18), rgba(13, 110, 253, .06)); + border-color: rgba(20, 184, 166, .18); +} + +.cc-kpi-meta { + font-size: .73rem; + line-height: 1.15; + color: #64748b; + font-weight: 600; +} + .cc-kpi-chart-card { border-radius: 1.2rem; overflow: hidden; diff --git a/call_center_pro.php b/call_center_pro.php index 3df45489..adc43d26 100644 --- a/call_center_pro.php +++ b/call_center_pro.php @@ -251,6 +251,7 @@ $allowedViews = [ 'pendientes_hoy' => 'Bandeja principal', 'nuevos_hoy' => 'Pedidos asignados hoy', 'confirmados' => 'Confirmados', + 'recuperados' => 'Recuperados', 'seguimiento' => 'Seguimiento', 'promo_final' => 'Promo Final', 'observados' => 'Observados', @@ -290,6 +291,7 @@ $stats = [ 'pendientes_hoy' => 0, 'nuevos_hoy' => 0, 'confirmados' => 0, + 'recuperados' => 0, 'seguimiento' => 0, 'promo_final' => 0, 'observados' => 0, @@ -462,6 +464,9 @@ try { if (in_array($order['estado'], cc_test_confirmed_states(), true)) { $stats['confirmados']++; } + if (cc_test_is_recovered_today($order, $todayStart)) { + $stats['recuperados']++; + } if ($order['estado'] === 'SE ENVIO NUMERO DE CUENTA' && !$order['es_promo_final']) { $stats['seguimiento']++; } @@ -474,26 +479,33 @@ try { } unset($order); - $performanceDashboard = cc_test_build_performance_dashboard($orders, $assessors, 7); + $performanceOrders = $orders; + if ($selectedAssessorFilter !== '') { + $performanceOrders = array_values(array_filter($orders, static function (array $order) use ($selectedAssessorFilter): bool { + return ($order['matches_assessor_filter'] ?? false) === true; + })); + } + + $performanceDashboard = cc_test_build_performance_dashboard($performanceOrders, $assessors, 7); $performanceTitle = $isAdmin ? ($selectedAssessorFilterLabel !== '' ? 'KPI diario · ' . $selectedAssessorFilterLabel : 'KPI diario por asesora') : 'Mi KPI diario'; $performanceSubtitle = $isAdmin - ? '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.'; + ? 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados 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. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados hoy.'; $performanceDashboardHtml = cc_test_render_performance_dashboard($performanceDashboard, [ 'title' => $performanceTitle, 'subtitle' => $performanceSubtitle, '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.', + 'chart1_note' => 'La dona separa los pedidos de hoy por estado: confirmados nuevos, recuperados, POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO, SE ENVIO NUMERO DE CUENTA, repetidos y cancelados.', '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 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.', + 'footnote' => 'Seguimiento abierto = pedidos asignados hoy en POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO y SE ENVIO NUMERO DE CUENTA que todavía no pasan a Promo Final.', ]); $panelSignature = cc_test_panel_orders_signature($orders); - $visibleOrders = array_values(array_filter($orders, static function (array $order) use ($view, $selectedAssessorFilter): bool { + $visibleOrders = array_values(array_filter($orders, static function (array $order) use ($view, $selectedAssessorFilter, $todayStart): bool { if ($selectedAssessorFilter !== '' && !($order['matches_assessor_filter'] ?? false)) { return false; } @@ -502,6 +514,7 @@ try { 'pendientes_hoy' => (bool) ($order['es_pendiente_hoy'] ?? false), 'nuevos_hoy' => (bool) ($order['es_nuevo_hoy'] ?? false), 'confirmados' => in_array(($order['estado'] ?? ''), cc_test_confirmed_states(), true), + 'recuperados' => cc_test_is_recovered_today($order, $todayStart), 'seguimiento' => ($order['estado'] ?? '') === 'SE ENVIO NUMERO DE CUENTA' && !($order['es_promo_final'] ?? false), 'promo_final' => (bool) ($order['es_promo_final'] ?? false), 'observados' => ($order['estado'] ?? '') === 'OBSERVADO' && !($order['es_promo_final'] ?? false), @@ -528,6 +541,20 @@ try { } } + if ($view === 'recuperados') { + $aRecovered = cc_test_dashboard_action_datetime($a); + $bRecovered = cc_test_dashboard_action_datetime($b); + if ($aRecovered && $bRecovered && $aRecovered != $bRecovered) { + return $bRecovered <=> $aRecovered; + } + if ($aRecovered && !$bRecovered) { + return -1; + } + if (!$aRecovered && $bRecovered) { + return 1; + } + } + if ($view === 'promo_final') { $aDays = (int) ($a['dias_seguimiento'] ?? 0); $bDays = (int) ($b['dias_seguimiento'] ?? 0); @@ -841,6 +868,16 @@ require_once 'layout_header.php'; +
+ +
+
+
Recuperados
+
+
+
+
+
diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index 38da4acb..db100fbd 100644 --- a/gestiones_callcenter.php +++ b/gestiones_callcenter.php @@ -164,6 +164,7 @@ $allowedViews = [ 'pendientes_hoy' => 'Bandeja principal', 'nuevos_hoy' => 'Nuevos de hoy', 'confirmados' => 'Confirmados', + 'recuperados' => 'Recuperados', 'seguimiento' => 'Seguimiento', 'promo_final' => 'Promo Final', 'observados' => 'Observados', @@ -206,6 +207,7 @@ $stats = [ 'pendientes_hoy' => 0, 'nuevos_hoy' => 0, 'confirmados' => 0, + 'recuperados' => 0, 'seguimiento' => 0, 'promo_final' => 0, 'observados' => 0, @@ -663,6 +665,9 @@ try { if (in_array($order['estado'], cc_test_confirmed_states(), true)) { $stats['confirmados']++; } + if (cc_test_is_recovered_today($order, $todayStart)) { + $stats['recuperados']++; + } if ($order['estado'] === 'SE ENVIO NUMERO DE CUENTA' && !$order['es_promo_final']) { $stats['seguimiento']++; } @@ -678,12 +683,12 @@ 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 asignados antes de hoy confirmados hoy.', + 'subtitle' => 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados 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.', + 'chart1_note' => 'La dona separa los pedidos de hoy por estado: confirmados nuevos, recuperados, POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO, SE ENVIO NUMERO DE CUENTA, repetidos y cancelados.', 'chart2_title' => 'Evolución de los últimos 7 días', '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.', + 'footnote' => 'Seguimiento abierto = pedidos asignados hoy en POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO y SE ENVIO NUMERO DE CUENTA que todavía no pasan a Promo Final.', ]); $orderedAssessorKeys = array_values(array_filter(cc_test_allowed_module_user_keys(), static function (string $key) use ($assessors): bool { @@ -753,11 +758,12 @@ try { ]; } - $visibleOrders = array_values(array_filter($orders, static function (array $order) use ($view): bool { + $visibleOrders = array_values(array_filter($orders, static function (array $order) use ($view, $todayStart): bool { return match ($view) { 'pendientes_hoy' => (bool) ($order['es_pendiente_hoy'] ?? false), 'nuevos_hoy' => (bool) ($order['es_nuevo_hoy'] ?? false), 'confirmados' => in_array(($order['estado'] ?? ''), cc_test_confirmed_states(), true), + 'recuperados' => cc_test_is_recovered_today($order, $todayStart), 'seguimiento' => ($order['estado'] ?? '') === 'SE ENVIO NUMERO DE CUENTA' && !($order['es_promo_final'] ?? false), 'promo_final' => (bool) ($order['es_promo_final'] ?? false), 'observados' => ($order['estado'] ?? '') === 'OBSERVADO' && !($order['es_promo_final'] ?? false), @@ -781,6 +787,20 @@ try { } } + if ($view === 'recuperados') { + $aRecovered = cc_test_dashboard_action_datetime($a); + $bRecovered = cc_test_dashboard_action_datetime($b); + if ($aRecovered && $bRecovered && $aRecovered != $bRecovered) { + return $bRecovered <=> $aRecovered; + } + if ($aRecovered && !$bRecovered) { + return -1; + } + if (!$aRecovered && $bRecovered) { + return 1; + } + } + if ($view === 'nuevos_hoy') { $aNew = cc_test_parse_datetime($a['first_seen_at'] ?? null); $bNew = cc_test_parse_datetime($b['first_seen_at'] ?? null); @@ -1182,6 +1202,16 @@ require_once 'layout_header.php';
+
+ +
+
+
Recuperados
+
+
+
+
+
diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index 92c60908..3163b5e3 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -660,6 +660,25 @@ function cc_test_dashboard_action_datetime(array $order): ?DateTimeImmutable return null; } +function cc_test_is_recovered_today(array $order, ?DateTimeImmutable $todayStart = null): bool +{ + $estado = cc_test_normalize_state((string) ($order['estado'] ?? '')); + if (!in_array($estado, cc_test_confirmed_states(), true)) { + return false; + } + + $todayStart = $todayStart ?? new DateTimeImmutable('today'); + $assignedDate = cc_test_followup_parse_datetime($order['assigned_at'] ?? null); + $actionDate = cc_test_dashboard_action_datetime($order); + + if (!$assignedDate || !$actionDate) { + return false; + } + + return $assignedDate->format('Y-m-d') < $todayStart->format('Y-m-d') + && $actionDate->format('Y-m-d') === $todayStart->format('Y-m-d'); +} + function cc_test_dashboard_bucket_key(array $order, array $assessors, bool $includeUnassigned = true): string { $assessorKey = trim((string) ($order['assessor_key'] ?? '')); @@ -738,11 +757,19 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in 'canceled_today' => 0, 'repeated_today' => 0, 'followup_open' => 0, + 'observados_today' => 0, 'confirm_rate_today' => 0.0, 'recovery_rate_today' => 0.0, + 'performance_today_rate' => 0.0, ]; $hasUnassignedBucket = false; + $followupStateCounts = [ + 'POR LLAMAR' => 0, + 'DEVOLVER LLAMADA' => 0, + 'OBSERVADO' => 0, + 'SE ENVIO NUMERO DE CUENTA' => 0, + ]; foreach ($orders as $order) { $summary['scope_total']++; @@ -766,8 +793,6 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in ]; } - $originDate = cc_test_dashboard_origin_datetime($order); - $originKey = $originDate ? $originDate->format('Y-m-d') : null; $assignedDate = cc_test_followup_parse_datetime($order['assigned_at'] ?? null); $assignedKey = $assignedDate ? $assignedDate->format('Y-m-d') : null; $actionDate = cc_test_dashboard_action_datetime($order); @@ -778,34 +803,46 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in $isConfirmed = in_array($estado, cc_test_confirmed_states(), true); $isCanceled = $estado === 'CANCELADO'; $isRepeated = $estado === 'REPETIDO'; - $isFollowupOpen = $estado === 'SE ENVIO NUMERO DE CUENTA' && !$isPromoFinal; + $isFollowupState = in_array($estado, cc_test_followup_tracking_states(), true); + $isFollowupOpen = $isFollowupState && !$isPromoFinal; - if ($assignedKey === $todayKey) { + $assignmentKey = $assignedKey; + $isAssignedToday = $assignmentKey === $todayKey; + + if ($isAssignedToday) { $bucketStats[$bucketKey]['assigned_today']++; $summary['assigned_today']++; - if (isset($trend[$assignedKey])) { - $trend[$assignedKey]['assigned_today']++; + if (isset($trend[$assignmentKey])) { + $trend[$assignmentKey]['assigned_today']++; + } + + if ($isFollowupOpen) { + $bucketStats[$bucketKey]['followup_open']++; + $summary['followup_open']++; + + if (isset($followupStateCounts[$estado])) { + $followupStateCounts[$estado]++; + } + + if ($estado === 'OBSERVADO') { + $summary['observados_today']++; + } } } - if ($isFollowupOpen) { - $bucketStats[$bucketKey]['followup_open']++; - $summary['followup_open']++; - } - - $confirmedReferenceKey = $assignedKey ?? $originKey; + $confirmedReferenceKey = $assignmentKey; if ($isConfirmed && $actionKey === $todayKey) { if ($confirmedReferenceKey !== null && $confirmedReferenceKey < $todayKey) { $bucketStats[$bucketKey]['recovered']++; $summary['confirmed_today_recovered']++; - } else { + } elseif ($isAssignedToday) { $bucketStats[$bucketKey]['confirmed_new']++; $summary['confirmed_today_new']++; } - if (isset($trend[$actionKey])) { + if ($isAssignedToday && isset($trend[$actionKey])) { if ($confirmedReferenceKey !== null && $confirmedReferenceKey < $actionKey) { $trend[$actionKey]['recovered']++; } else { @@ -814,7 +851,7 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in } } - if ($isCanceled && $actionKey === $todayKey) { + if ($isAssignedToday && $isCanceled && $actionKey === $todayKey) { $bucketStats[$bucketKey]['canceled']++; $summary['canceled_today']++; @@ -823,7 +860,7 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in } } - if ($isRepeated && $actionKey === $todayKey) { + if ($isAssignedToday && $isRepeated && $actionKey === $todayKey) { $bucketStats[$bucketKey]['repeated']++; $summary['repeated_today']++; } @@ -836,6 +873,9 @@ function cc_test_build_performance_dashboard(array $orders, array $assessors, in $summary['recovery_rate_today'] = $summary['confirmed_today_total'] > 0 ? round(($summary['confirmed_today_recovered'] / $summary['confirmed_today_total']) * 100, 1) : 0.0; + $summary['performance_today_rate'] = $summary['assigned_today'] > 0 + ? round(($summary['confirmed_today_total'] / $summary['assigned_today']) * 100, 1) + : 0.0; $advisorLabels = []; $advisorAssigned = []; @@ -898,28 +938,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'], + 'labels' => ['Confirmados nuevos', 'Recuperados', 'Por llamar', 'Devolver llamada', 'Observados', 'Se envió número de cuenta', 'Repetidos', 'Cancelados'], 'datasets' => [ [ 'label' => 'Estados', 'data' => [ (int) ($summary['confirmed_today_new'] ?? 0), (int) ($summary['confirmed_today_recovered'] ?? 0), - (int) ($summary['followup_open'] ?? 0), + (int) ($followupStateCounts['POR LLAMAR'] ?? 0), + (int) ($followupStateCounts['DEVOLVER LLAMADA'] ?? 0), + (int) ($followupStateCounts['OBSERVADO'] ?? 0), + (int) ($followupStateCounts['SE ENVIO NUMERO DE CUENTA'] ?? 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(20, 184, 166, 0.84)', 'rgba(13, 110, 253, 0.88)', + 'rgba(13, 202, 240, 0.86)', + 'rgba(255, 193, 7, 0.82)', + 'rgba(253, 126, 20, 0.84)', '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, 148, 136, 1)', 'rgba(13, 110, 253, 1)', + 'rgba(13, 202, 240, 1)', + 'rgba(245, 158, 11, 1)', + 'rgba(253, 126, 20, 1)', 'rgba(108, 117, 125, 1)', 'rgba(220, 53, 69, 1)', ], @@ -1025,12 +1074,12 @@ 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 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.')); + $subtitle = trim((string) ($options['subtitle'] ?? 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados hoy.')); + $chart1Title = trim((string) ($options['chart1_title'] ?? 'Distribución de estados de hoy')); + $chart1Note = trim((string) ($options['chart1_note'] ?? 'La dona separa los pedidos de hoy por estado: confirmados nuevos, recuperados, POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO, SE ENVIO NUMERO DE CUENTA, repetidos y cancelados.')); $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 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.')); + $footnote = trim((string) ($options['footnote'] ?? 'Seguimiento abierto = pedidos asignados hoy en POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO y 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 === '') { $sectionId = 'ccPerformanceDashboard'; @@ -1038,27 +1087,38 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options = $summary = (array) ($dashboard['summary'] ?? []); $trendChart = (array) ($dashboard['trend_chart'] ?? []); - $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); + $statusChart = (array) ($dashboard['status_chart'] ?? []); + $statusChartLabels = array_values((array) ($statusChart['labels'] ?? [])); + $statusChartDataset = (array) (($statusChart['datasets'][0] ?? []) ?: []); + $statusChartValues = array_map(static function ($value) { + return (int) $value; + }, array_values((array) ($statusChartDataset['data'] ?? []))); + $statusChartColors = is_array($statusChartDataset['backgroundColor'] ?? null) + ? array_values($statusChartDataset['backgroundColor']) + : []; + $statusTotal = array_sum($statusChartValues); + $confirmedTodayNew = (int) ($summary['confirmed_today_new'] ?? 0); + $confirmedTodayRecovered = (int) ($summary['confirmed_today_recovered'] ?? 0); + $confirmedTodayTotal = (int) ($summary['confirmed_today_total'] ?? 0); + $performanceTodayRate = (float) ($summary['performance_today_rate'] ?? 0); + $performanceTodayMeta = sprintf( + '%d confirmado%s = %d nuevo%s + %d recuperado%s', + $confirmedTodayTotal, + $confirmedTodayTotal === 1 ? '' : 's', + $confirmedTodayNew, + $confirmedTodayNew === 1 ? '' : 's', + $confirmedTodayRecovered, + $confirmedTodayRecovered === 1 ? '' : 's' + ); $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) { + foreach ($statusChartValues as $index => $value) { $statusLegendItems[] = [ - 'label' => $row['label'], - 'value' => (int) $row['value'], - 'percentage' => round(((float) $row['value'] / $statusTotal) * 100, 1), - 'color' => $row['color'], + 'label' => (string) ($statusChartLabels[$index] ?? ('Estado ' . ($index + 1))), + 'value' => (int) $value, + 'percentage' => round(((float) $value / $statusTotal) * 100, 1), + 'color' => (string) ($statusChartColors[$index] ?? 'rgba(108, 117, 125, 0.65)'), ]; } } else { @@ -1072,8 +1132,9 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options = $badges = [ ['label' => 'Asignados hoy', 'value' => (int) ($summary['assigned_today'] ?? 0), 'class' => 'cc-kpi-pill-primary'], - ['label' => 'Confirmados hoy', 'value' => (int) ($summary['confirmed_today_total'] ?? 0), 'class' => 'cc-kpi-pill-success'], - ['label' => 'Recuperados', 'value' => (int) ($summary['confirmed_today_recovered'] ?? 0), 'class' => 'cc-kpi-pill-warning'], + ['label' => 'Confirmados hoy', 'value' => $confirmedTodayTotal, 'class' => 'cc-kpi-pill-success'], + ['label' => 'Recuperados', 'value' => $confirmedTodayRecovered, 'class' => 'cc-kpi-pill-warning'], + ['label' => 'Rendimiento del día', 'value' => number_format($performanceTodayRate, 1) . '%', 'class' => 'cc-kpi-pill-total', 'meta' => $performanceTodayMeta], ['label' => 'Seguimiento abierto', 'value' => (int) ($summary['followup_open'] ?? 0), 'class' => 'cc-kpi-pill-info'], ['label' => 'Cancelados hoy', 'value' => (int) ($summary['canceled_today'] ?? 0), 'class' => 'cc-kpi-pill-danger'], ['label' => '% confirmación', 'value' => number_format((float) ($summary['confirm_rate_today'] ?? 0), 1) . '%', 'class' => 'cc-kpi-pill-dark'], @@ -1114,6 +1175,9 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options =
+ + +
@@ -1141,7 +1205,7 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options =
-
Detalle por estado (%)
+
Detalle del KPI de hoy (%)
@@ -1211,8 +1275,11 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options = ? statusDataset.backgroundColor : [ 'rgba(25, 135, 84, 0.9)', - 'rgba(255, 193, 7, 0.92)', + 'rgba(20, 184, 166, 0.84)', 'rgba(13, 110, 253, 0.88)', + 'rgba(13, 202, 240, 0.86)', + 'rgba(255, 193, 7, 0.82)', + 'rgba(253, 126, 20, 0.84)', 'rgba(108, 117, 125, 0.84)', 'rgba(220, 53, 69, 0.9)' ]; @@ -1220,8 +1287,11 @@ function cc_test_render_performance_dashboard(array $dashboard, array $options = ? statusDataset.borderColor : [ 'rgba(25, 135, 84, 1)', - 'rgba(245, 158, 11, 1)', + 'rgba(13, 148, 136, 1)', 'rgba(13, 110, 253, 1)', + 'rgba(13, 202, 240, 1)', + 'rgba(245, 158, 11, 1)', + 'rgba(253, 126, 20, 1)', 'rgba(108, 117, 125, 1)', 'rgba(220, 53, 69, 1)' ];