From 65e286876e3409ab474b13f4827bde43ef84a529 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 12 Aug 2026 00:15:21 +0000 Subject: [PATCH] Autosave: 20260812-001715 --- call_center_pro.php | 275 ++++++++++++++++++++++++++- gestiones_callcenter.php | 83 ++++++-- includes/callcenter_test_helpers.php | 2 +- 3 files changed, 336 insertions(+), 24 deletions(-) diff --git a/call_center_pro.php b/call_center_pro.php index 1bfab7ad..588a74b6 100644 --- a/call_center_pro.php +++ b/call_center_pro.php @@ -397,6 +397,12 @@ $viewCards = $isTuaniRecuperablesStore ? [ 'stat_key' => 'nuevos_hoy', 'active_class' => 'bg-primary-subtle border border-primary', ], + 'por_llamar' => [ + 'label' => 'Por llamar', + 'heading' => 'Pedidos por llamar', + 'stat_key' => 'por_llamar', + 'active_class' => 'bg-info-subtle border border-info', + ], 'ultimos_3_dias' => [ 'label' => 'Últimos 4 días hábiles', 'heading' => 'Pedidos asignados de los últimos 4 días hábiles', @@ -494,6 +500,7 @@ $stats = [ 'total' => 0, 'pendientes_hoy' => 0, 'nuevos_hoy' => 0, + 'por_llamar' => 0, 'ultimos_3_dias' => 0, 'confirmados' => 0, 'recuperados' => 0, @@ -502,6 +509,13 @@ $stats = [ 'observados' => 0, 'cerrados' => 0, ]; +$assessorFloatingAlertEnabled = false; +$assessorFloatingPorLlamarCount = 0; +$assessorFloatingNewAssignedCount = 0; +$assessorFloatingOpenCount = 0; +$assessorFloatingAlertSignature = ''; +$assessorFloatingAlertStorageKey = ''; +$assessorFloatingAlertTargetView = 'pendientes_hoy'; $repartoSettings = [ 'store_key' => $storeKey, @@ -829,6 +843,9 @@ try { if ($order['es_pendiente_hoy']) { $stats['pendientes_hoy']++; } + if ($order['estado'] === 'POR LLAMAR' && !$order['es_promo_final']) { + $stats['por_llamar']++; + } if (in_array($order['estado'], cc_test_confirmed_states(), true)) { $stats['confirmados']++; } @@ -930,6 +947,7 @@ try { return match ($view) { 'pendientes_hoy' => (bool) ($order['es_pendiente_hoy'] ?? false), 'nuevos_hoy' => (bool) ($order['es_nuevo_hoy'] ?? false), + 'por_llamar' => ($order['estado'] ?? '') === 'POR LLAMAR' && !($order['es_promo_final'] ?? false), 'ultimos_3_dias' => (bool) ($order['es_ultimos_3_dias'] ?? false), 'confirmados' => in_array(($order['estado'] ?? ''), cc_test_confirmed_states(), true), 'recuperados' => cc_test_is_recovered_today($order, $todayStart), @@ -1018,7 +1036,7 @@ try { } } - if ($view === "pendientes_hoy") { + if ($view === "pendientes_hoy" || $view === 'por_llamar') { $aPendientePromoFinal = !empty($a['promo_final_pendiente']); $bPendientePromoFinal = !empty($b['promo_final_pendiente']); if ($aPendientePromoFinal !== $bPendientePromoFinal) { @@ -1112,6 +1130,44 @@ try { ? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' pendiente' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' de Promo Final. Mientras existan pendientes de sustento, los pedidos de Día 1 mantienen el número oculto; cuando ya no queden pendientes, el celular volverá a mostrarse.' : ''; + if (!$isAdmin && !$isTuaniRecuperablesStore) { + $assessorFloatingAlertSourceTokens = []; + + foreach ($orders as $floatingOrder) { + $floatingState = cc_test_normalize_state((string) ($floatingOrder['estado'] ?? '')); + $floatingIsPromoFinal = !empty($floatingOrder['es_promo_final']); + $floatingIsOpen = !$floatingIsPromoFinal && in_array($floatingState, $openStates, true); + + if (!$floatingIsOpen) { + continue; + } + + $assessorFloatingOpenCount++; + if ($floatingState === 'POR LLAMAR') { + $assessorFloatingPorLlamarCount++; + } + if (!empty($floatingOrder['es_nuevo_hoy'])) { + $assessorFloatingNewAssignedCount++; + } + + $assessorFloatingAlertSourceTokens[] = implode('|', [ + (string) ($floatingOrder['source_key'] ?? ''), + $floatingState, + trim((string) ($floatingOrder['assigned_at'] ?? '')), + ]); + } + + sort($assessorFloatingAlertSourceTokens); + $assessorFloatingAlertEnabled = $assessorFloatingOpenCount > 0; + $assessorFloatingAlertSignature = $assessorFloatingAlertEnabled + ? sha1(json_encode($assessorFloatingAlertSourceTokens, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) + : ''; + $assessorFloatingAlertStorageKey = 'cc-assessor-floating-alert:v1:' . $storeKey . ':' . $currentUserId; + $assessorFloatingAlertTargetView = $assessorFloatingPorLlamarCount > 0 + ? 'por_llamar' + : ($assessorFloatingNewAssignedCount > 0 ? 'nuevos_hoy' : 'pendientes_hoy'); + } + $visibleOrdersTotalCount = count($visibleOrders); $todosPageSize = 200; $todosPage = 1; @@ -1182,6 +1238,7 @@ if ($selectedAssessorFilter !== '') { } $panelOrdersSignature = isset($visibleOrders) && is_array($visibleOrders) ? cc_test_panel_orders_signature($visibleOrders) : ''; +$panelInboxSignature = (!$isAdmin && !$isTuaniRecuperablesStore) ? $assessorFloatingAlertSignature : ''; if (!empty($_GET['ajax_refresh'])) { header('Content-Type: application/json; charset=utf-8'); if ($errorMessage !== null) { @@ -1193,6 +1250,11 @@ if (!empty($_GET['ajax_refresh'])) { echo json_encode([ 'success' => true, 'signature' => $panelOrdersSignature, + 'inbox_signature' => $panelInboxSignature, + 'alert_signature' => $assessorFloatingAlertSignature, + 'por_llamar_count' => $assessorFloatingPorLlamarCount, + 'new_assigned_count' => $assessorFloatingNewAssignedCount, + 'open_count' => $assessorFloatingOpenCount, 'count' => $visibleOrdersTotalCount, 'view' => $view, 'store' => $storeKey, @@ -1410,9 +1472,171 @@ require_once 'layout_header.php'; color: var(--bs-primary-text-emphasis) !important; } +.cc-assessor-floating-alert { + position: fixed; + right: 0; + top: 42%; + z-index: 1055; + width: min(18rem, calc(100vw - 1rem)); + color: #fff; + background: linear-gradient(135deg, #0f766e 0%, #0ea5e9 100%); + border-radius: 1.35rem 0 0 1.35rem; + box-shadow: 0 1.15rem 2.5rem rgba(14, 116, 144, .28); + overflow: hidden; + isolation: isolate; + transition: transform .2s ease, box-shadow .2s ease; +} + +.cc-assessor-floating-alert::before { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient(circle at 15% 0%, rgba(255, 255, 255, .28), transparent 38%); + pointer-events: none; + z-index: -1; +} + +.cc-assessor-floating-alert:hover { + transform: translateX(-.28rem); + box-shadow: 0 1.35rem 2.8rem rgba(14, 116, 144, .34); +} + +.cc-assessor-floating-alert.is-dismissed { + display: none; +} + +.cc-assessor-floating-alert__link { + display: block; + padding: 1rem 1rem 1rem 1.15rem; + color: inherit; + text-decoration: none; +} + +.cc-assessor-floating-alert__kicker { + display: inline-flex; + align-items: center; + gap: .35rem; + font-size: .72rem; + font-weight: 800; + text-transform: uppercase; + letter-spacing: .06em; + opacity: .88; +} + +.cc-assessor-floating-alert__number { + font-size: 2.25rem; + line-height: 1; + font-weight: 900; + letter-spacing: -.04em; +} + +.cc-assessor-floating-alert__title { + font-weight: 800; + line-height: 1.15; +} + +.cc-assessor-floating-alert__text { + font-size: .82rem; + opacity: .9; +} + +.cc-assessor-floating-alert__chips { + display: flex; + flex-wrap: wrap; + gap: .35rem; + margin-top: .75rem; +} + +.cc-assessor-floating-alert__chip { + display: inline-flex; + align-items: center; + gap: .25rem; + border: 1px solid rgba(255, 255, 255, .34); + background: rgba(255, 255, 255, .16); + border-radius: 999px; + padding: .28rem .55rem; + font-size: .74rem; + font-weight: 700; + backdrop-filter: blur(8px); +} + +.cc-assessor-floating-alert__close { + position: absolute; + top: .42rem; + right: .42rem; + z-index: 2; + width: 1.6rem; + height: 1.6rem; + border: 1px solid rgba(255, 255, 255, .28); + border-radius: 999px; + background: rgba(15, 23, 42, .18); + color: #fff; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.cc-assessor-floating-alert__close:hover { + background: rgba(15, 23, 42, .28); +} + +@media (max-width: 767.98px) { + .cc-assessor-floating-alert { + top: auto; + right: .75rem; + bottom: .85rem; + left: .75rem; + width: auto; + border-radius: 1.25rem; + } + + .cc-assessor-floating-alert:hover { + transform: translateY(-.15rem); + } +} +
+ + 0) { + $assessorFloatingAlertMainCount = $assessorFloatingPorLlamarCount; + $assessorFloatingAlertTitle = 'Por llamar'; + $assessorFloatingAlertText = 'Prioriza estos pedidos pendientes.'; + } else { + $assessorFloatingAlertMainCount = $assessorFloatingOpenCount; + $assessorFloatingAlertTitle = 'Pendientes'; + $assessorFloatingAlertText = 'Revisa tu bandeja principal.'; + } + $assessorFloatingAlertUrl = cc_test_build_url('call_center_pro.php', array_merge($callCenterParams, ['view' => $assessorFloatingAlertTargetView])); + ?> + + +
@@ -2474,9 +2698,11 @@ ccCallCenterPanelBaseUrl.hash = ''; const ccCallCenterPanelRefreshUrl = new URL(ccCallCenterPanelBaseUrl.toString()); ccCallCenterPanelRefreshUrl.searchParams.set('ajax_refresh', '1'); const ccCallCenterPanelInitialSignature = ; +const ccCallCenterPanelInitialInboxSignature = ; const ccCallCenterPanelRefreshMs = 300000; let ccCallCenterPanelRefreshInFlight = false; let ccCallCenterPanelLastSignature = ccCallCenterPanelInitialSignature; +let ccCallCenterPanelLastInboxSignature = ccCallCenterPanelInitialInboxSignature; const ccCallCenterTimerStorageKey = 'cc-callcenter-modal-timers:v1'; @@ -3569,6 +3795,41 @@ function ccCallCenterPanelRefreshNow(event) { return false; } +function ccAssessorFloatingAlertInit() { + const alert = document.getElementById('assessorFloatingAlert'); + if (!alert) { + return; + } + + const signature = String(alert.dataset.alertSignature || ''); + const storageKey = String(alert.dataset.alertStorageKey || ''); + try { + if (signature && storageKey && window.localStorage.getItem(storageKey) === signature) { + alert.classList.add('is-dismissed'); + } + } catch (error) { + // Si localStorage no está disponible, la alerta solo queda visible en esta sesión. + } + + const closeButton = document.getElementById('assessorFloatingAlertClose'); + if (!closeButton) { + return; + } + + closeButton.addEventListener('click', function (event) { + event.preventDefault(); + event.stopPropagation(); + try { + if (signature && storageKey) { + window.localStorage.setItem(storageKey, signature); + } + } catch (error) { + // No bloqueamos la pantalla si el navegador impide guardar preferencias. + } + alert.classList.add('is-dismissed'); + }); +} + function ccCallCenterPanelMarkDirty(event) { const target = event.target; if (!target || !target.form) { @@ -3604,7 +3865,7 @@ function ccCallCenterPanelBindDirtyTracking() { } async function ccCallCenterPanelCheckForUpdates() { - if (ccCallCenterPanelRefreshInFlight || document.visibilityState !== 'visible' || ccCallCenterPanelShouldPauseRefresh() || !ccCallCenterPanelInitialSignature) { + if (ccCallCenterPanelRefreshInFlight || document.visibilityState !== 'visible' || ccCallCenterPanelShouldPauseRefresh()) { return; } @@ -3628,12 +3889,21 @@ async function ccCallCenterPanelCheckForUpdates() { return; } + const inboxSignature = String(data.inbox_signature || ''); + if (inboxSignature && inboxSignature !== ccCallCenterPanelLastInboxSignature) { + ccCallCenterPanelNavigate(); + return; + } + const signature = String(data.signature || ''); if (signature && signature !== ccCallCenterPanelLastSignature) { ccCallCenterPanelNavigate(); return; } + if (inboxSignature) { + ccCallCenterPanelLastInboxSignature = inboxSignature; + } if (signature) { ccCallCenterPanelLastSignature = signature; } @@ -3645,6 +3915,7 @@ async function ccCallCenterPanelCheckForUpdates() { } syncServerAssessorSelects(); +ccAssessorFloatingAlertInit(); ccCallCenterPanelBindDirtyTracking(); ccCallCenterPanelRestoreScroll(); window.addEventListener('beforeunload', ccCallCenterPanelSaveScroll); diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index 1bfe9df7..d23358ab 100644 --- a/gestiones_callcenter.php +++ b/gestiones_callcenter.php @@ -703,7 +703,7 @@ try { } } - // Contamos cuántos pedidos siguen activos (POR LLAMAR / DEVOLVER LLAMADA / OBSERVADO). + // Contamos cuántos pedidos siguen activos (POR LLAMAR / DEVOLVER LLAMADA / OBSERVADO / SE ENVIO NUMERO DE CUENTA). foreach ($orders as $o) { if ($o['user_id'] === null) { continue; @@ -1377,6 +1377,47 @@ require_once 'layout_header.php'; border-left: .45rem solid var(--cc-assessor-accent, #adb5bd) !important; background-color: rgba(var(--cc-assessor-accent-rgb, 173, 181, 189), .16); box-shadow: inset 0 0 0 1px rgba(var(--cc-assessor-accent-rgb, 173, 181, 189), .22); + position: relative; +} + +.cc-callcenter-assessor-summary-card .card-body { + position: relative; +} + +.cc-callcenter-assessor-summary-card.has-pending .card-body { + padding-bottom: 3.45rem !important; +} + +.cc-callcenter-pending-tab { + position: absolute; + right: .9rem; + bottom: .9rem; + z-index: 2; + display: inline-flex; + align-items: center; + gap: .35rem; + padding: .42rem .82rem; + border: 1px solid rgba(255, 193, 7, .28) !important; + background: linear-gradient(135deg, rgba(255, 193, 7, .22), rgba(255, 255, 255, .92)); + color: #8a5a00; + font-weight: 700; + line-height: 1; + white-space: nowrap; + letter-spacing: .01em; + box-shadow: 0 .85rem 1.65rem rgba(15, 23, 42, .16), inset 0 1px 0 rgba(255, 255, 255, .55); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + transform: translateY(24%); +} + +.cc-callcenter-pending-tab::before { + content: ""; + width: .5rem; + height: .5rem; + border-radius: 999px; + background: #f0ad4e; + box-shadow: 0 0 0 .18rem rgba(255, 193, 7, .16); + flex: 0 0 auto; } .cc-callcenter-assign-form { @@ -1852,21 +1893,21 @@ require_once 'layout_header.php';
-
-
-
+
" style=""> +
+
- 0): ?> - Pendientes: - -
-
-
Cupo restante ()
-
+
+
Cupo restante ()
+
+
+ 0): ?> + Pendientes: +
@@ -1909,21 +1950,21 @@ require_once 'layout_header.php';
-
-
-
+
" style=""> +
+
- 0): ?> - Pendientes: - -
-
-
Cupo restante ()
-
+
+
Cupo restante ()
+
+
+ 0): ?> + Pendientes: +
diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index 0f8185e5..2782a5f7 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -1420,7 +1420,7 @@ function cc_test_valid_states(): array if (!function_exists('cc_test_open_states')) { function cc_test_open_states(): array { - return ['POR LLAMAR', 'DEVOLVER LLAMADA', 'OBSERVADO']; + return cc_test_followup_tracking_states(); } }