@extends('fragments.fullTemplate') @section('titulo') {{ $tituloModulo }} @endsection @section('estilos') @endsection @section('contenido') @php $chatLabelColor = function ($badge) { $mapa = [ 'warning' => 'yellow', 'info' => 'blue', 'success' => 'green', 'danger' => 'red', 'secondary' => 'grey', 'primary' => 'blue', 'dark' => 'grey', 'light' => 'grey basic', ]; return $mapa[(string) $badge] ?? 'grey'; }; @endphp @php $chatActivoId = $chatActivo ? (int) $chatActivo->id_portal_chat_conversacion : 0; @endphp
@if(session()->has('success')) @endif @if(session()->has('warning')) @endif @if(session()->has('error')) @endif @if (count($errors) > 0) @endif
{{ $tituloModulo }}

{{ $subtituloModulo }}

@if($conversacionesPendientes->total() > 0)

Mostrando registros del {{ $conversacionesPendientes->firstItem() }} al {{ $conversacionesPendientes->lastItem() }} de un total de {{ $conversacionesPendientes->total() }}.

@else

No hay chats pendientes de asignación.

@endif
@forelse($conversacionesPendientes as $conversacion) @empty @endforelse
Cliente / Usuario portal Correo electrónico Empresas relacionadas Asunto Último mensaje Tiempo relativo Estado Acciones
{{ $conversacion->cliente_portal }} {{ $conversacion->correo_portal }} @if($conversacion->empresa_factura_nombre !== '')
{{ $conversacion->empresa_factura_nombre }}
Empresa factura
@endif @foreach($conversacion->empresas_informe as $empresaInforme)
{{ $empresaInforme }}
Empresa informe
@endforeach @if($conversacion->empresa_factura_nombre === '' && empty($conversacion->empresas_informe)) Sin empresas vinculadas @endif
{{ $conversacion->asunto_mostrar }} {{ $conversacion->ultimo_mensaje_mostrar }} {{ $conversacion->tiempo_relativo }} {{ $conversacion->estado_mostrar }}
{{ csrf_field() }}
No hay chats pendientes de asignación en este momento.
@if($conversacionesPendientes->hasPages())
{{ $conversacionesPendientes->appends(request()->query())->links('pagination::bootstrap-4') }}
@endif
Conversación activa
@include('Portal_Clientes.Chat.partials.mis_chats_sidebar', [ 'misChats' => $misChats, 'chatActivoId' => $chatActivoId, ])
Conversación activa
@if($chatActivo)
{{ $chatActivo->asunto_mostrar }}

Cliente: {{ $chatActivo->cliente_portal }}

Correo: {{ $chatActivo->correo_portal }}

Empresas vinculadas: @if($chatActivo->empresa_factura_nombre !== '')
{{ $chatActivo->empresa_factura_nombre }} Empresa factura
@endif @foreach($chatActivo->empresas_informe as $empresaInforme)
{{ $empresaInforme }} Empresa informe
@endforeach @if($chatActivo->empresa_factura_nombre === '' && empty($chatActivo->empresas_informe))
Sin empresas vinculadas
@endif
{{ $chatActivo->estado_mostrar }} @if($puedeResponderChatActivo)
{{ csrf_field() }}
@endif
@include('Portal_Clientes.Chat.partials.messages', ['mensajes' => $mensajesChatActivo])
{{ csrf_field() }}
@else
Selecciona una conversación del panel izquierdo para ver el detalle.
@endif
@endsection @section('scriptBeforeAjax') var $chatRoot = $('#chat-admin-mis-chats'); if ($chatRoot.length) { var pollTimer = null; var pollInFlight = false; var historyRefreshInFlight = false; var sendInFlight = false; var pollInterval = parseInt($chatRoot.data('poll-interval'), 10) || 7000; var audioContext = null; function getActiveChatId() { return parseInt($chatRoot.attr('data-active-chat-id'), 10) || 0; } function setActiveChatId(chatId) { $chatRoot.attr('data-active-chat-id', chatId || ''); } function buildPollUrl(chatId) { return String($chatRoot.data('poll-url-template')).replace('__CHAT_ID__', chatId); } function buildMessagesUrl(chatId) { return String($chatRoot.data('messages-url-template')).replace('__CHAT_ID__', chatId); } function getHistoryLastMessageId() { return parseInt($('#chat-admin-history').attr('data-last-message-id'), 10) || 0; } function setHistoryLastMessageId(messageId) { $('#chat-admin-history').attr('data-last-message-id', messageId || 0); } function updateTextareaHeight() { var $input = $('#chat-admin-message-input'); if (!$input.length) { return; } $input.css('height', 'auto'); $input.css('height', Math.min($input[0].scrollHeight, 144) + 'px'); } function escapeHtml(text) { return $('
').text(text || '').html(); } function showFeedback(type, message) { var $feedback = $('#chat-admin-feedback'); if (!$feedback.length) { return; } $feedback .removeClass('alert-success alert-danger alert-warning alert-info') .addClass('alert-' + type) .html(message) .show(); } function hideFeedback() { $('#chat-admin-feedback').hide().html(''); } function renderConversationCompanies(chat) { var $container = $('#chat-admin-active-companies'); if (!$container.length) { return; } $container.empty().append($('').text('Empresas vinculadas:')); var hasCompanies = false; var empresaFactura = $.trim(chat && chat.empresa_factura_nombre ? chat.empresa_factura_nombre : ''); if (empresaFactura !== '') { hasCompanies = true; $container.append( $('
') .append($('').text(empresaFactura)) .append(' ') .append($('').addClass('text-muted small').text('Empresa factura')) ); } $.each((chat && chat.empresas_informe) || [], function(index, empresaInforme) { empresaInforme = $.trim(empresaInforme || ''); if (empresaInforme === '') { return; } hasCompanies = true; $container.append( $('
') .append($('').text(empresaInforme)) .append(' ') .append($('').addClass('text-muted small').text('Empresa informe')) ); }); if (!hasCompanies) { $container.append($('
').addClass('text-muted').text('Sin empresas vinculadas')); } } function chatSemanticLabelColor(badge) { var colors = { warning: 'yellow', info: 'blue', success: 'green', danger: 'red', secondary: 'grey', primary: 'blue', dark: 'grey', light: 'grey basic' }; return colors[badge] || 'grey'; } function updateConversationHeader(chat) { if (!chat) { return; } setActiveChatId(chat.id_portal_chat_conversacion); $('#chat-admin-active-subject').text(chat.asunto_mostrar || ''); $('#chat-admin-active-client').text(chat.cliente_portal || ''); $('#chat-admin-active-email').text(chat.correo_portal || ''); renderConversationCompanies(chat); $('#chat-admin-active-status') .attr('class', 'ui ' + chatSemanticLabelColor(chat.estado_badge || 'primary') + ' label') .text(chat.estado_mostrar || 'Asignado'); } function updateSidebar(html) { if (typeof html === 'string') { $('#chat-admin-sidebar-body').html(html); } } function updateReplyAvailability(canReply) { if (canReply) { $('#chat-admin-composer-wrap').removeClass('d-none'); $('#chat-admin-closed-alert').addClass('d-none'); return; } $('#chat-admin-composer-wrap').addClass('d-none'); $('#chat-admin-closed-alert').removeClass('d-none'); } function isHistoryNearBottom() { var $history = $('#chat-admin-history'); if (!$history.length) { return false; } var element = $history.get(0); return (element.scrollHeight - element.scrollTop - $history.outerHeight()) < 120; } function scrollHistoryToBottom() { var $history = $('#chat-admin-history'); if (!$history.length) { return; } $history.scrollTop($history.get(0).scrollHeight); } function removeHistoryEmptyState() { $('#chat-admin-history-empty').remove(); } function getRenderedLastMessageId() { var $lastMessage = $('#chat-admin-history').find('.chat-message-row').last(); if (!$lastMessage.length) { return 0; } return parseInt($lastMessage.attr('data-message-id'), 10) || 0; } function sortHistoryRows() { var $thread = $('#chat-admin-thread'); if (!$thread.length) { return; } var rows = $thread.children('.chat-message-row').get(); rows.sort(function(left, right) { var leftId = parseInt($(left).attr('data-message-id'), 10) || 0; var rightId = parseInt($(right).attr('data-message-id'), 10) || 0; return leftId - rightId; }); $.each(rows, function(index, row) { $thread.append(row); }); } function replaceHistoryHtml(html, shouldScroll, shouldPlaySound, fallbackLastMessageId) { $('#chat-admin-history').html(html); sortHistoryRows(); setHistoryLastMessageId(getRenderedLastMessageId() || fallbackLastMessageId || 0); if (shouldScroll) { scrollHistoryToBottom(); } if (shouldPlaySound) { playIncomingSound(); } } function reloadConversationHistory(chatId, shouldScroll, shouldPlaySound, fallbackLastMessageId) { if (!chatId) { return $.Deferred().resolve().promise(); } historyRefreshInFlight = true; return $.ajax({ url: buildMessagesUrl(chatId), type: 'GET', dataType: 'html', headers: { 'X-Requested-With': 'XMLHttpRequest' } }).done(function(html) { replaceHistoryHtml(html, shouldScroll, shouldPlaySound, fallbackLastMessageId); }).fail(function(xhr) { if (xhr && (xhr.status === 403 || xhr.status === 404)) { window.location.href = $chatRoot.data('index-url'); } }).always(function() { historyRefreshInFlight = false; }); } function playIncomingSound() { try { var AudioCtx = window.AudioContext || window.webkitAudioContext; if (!AudioCtx) { return; } if (!audioContext) { audioContext = new AudioCtx(); } if (audioContext.state === 'suspended') { audioContext.resume(); } var oscillator = audioContext.createOscillator(); var gainNode = audioContext.createGain(); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(880, audioContext.currentTime); gainNode.gain.setValueAtTime(0.001, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.08, audioContext.currentTime + 0.01); gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.18); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.start(); oscillator.stop(audioContext.currentTime + 0.18); } catch (error) { console.log('No fue posible reproducir el sonido del chat.', error); } } function resetSelectedFile() { $('#chat-admin-selected-file').addClass('d-none').text(''); } function updateSelectedFile() { var input = $('#chat-admin-file-input').get(0); if (!input || !input.files || !input.files.length) { resetSelectedFile(); return; } $('#chat-admin-selected-file') .removeClass('d-none') .text('Archivo seleccionado: ' + input.files[0].name); } function resolveErrorMessage(xhr) { if (!xhr || !xhr.responseJSON) { return 'No fue posible completar la operación.'; } if (xhr.responseJSON.message) { return xhr.responseJSON.message; } if (xhr.responseJSON.errors) { var firstKey = Object.keys(xhr.responseJSON.errors)[0]; if (firstKey && xhr.responseJSON.errors[firstKey].length) { return xhr.responseJSON.errors[firstKey][0]; } } return 'No fue posible completar la operación.'; } function shouldPollChat() { return getActiveChatId() > 0 && $('#tab-mis-chats').hasClass('active'); } function scheduleChatPoll() { clearTimeout(pollTimer); if (!shouldPollChat()) { return; } pollTimer = setTimeout(function() { runChatPoll(); }, pollInterval); } function runChatPoll() { if (pollInFlight || historyRefreshInFlight || !shouldPollChat()) { scheduleChatPoll(); return; } pollInFlight = true; var nearBottom = isHistoryNearBottom(); var currentLastMessageId = getHistoryLastMessageId(); $.ajax({ url: buildPollUrl(getActiveChatId()), type: 'GET', dataType: 'json', data: { ultimo_id_mensaje: currentLastMessageId }, success: function(response) { if (response.chat_activo) { updateConversationHeader(response.chat_activo); } updateSidebar(response.mis_chats_html); updateReplyAvailability(response.puede_responder); var ultimoIdMensaje = parseInt(response.ultimo_id_mensaje, 10) || 0; var hayMensajesNuevos = !!response.hay_mensajes_nuevos || ultimoIdMensaje > currentLastMessageId; if (response.hay_mensajes_cliente_nuevos && typeof refreshSystemNotifications === 'function') { refreshSystemNotifications(); } if (!hayMensajesNuevos) { setHistoryLastMessageId(ultimoIdMensaje); return; } reloadConversationHistory( getActiveChatId(), nearBottom, !!response.hay_mensajes_cliente_nuevos, ultimoIdMensaje ); }, error: function(xhr) { if (xhr.status === 403 || xhr.status === 404) { window.location.href = $chatRoot.data('index-url'); } }, complete: function() { pollInFlight = false; scheduleChatPoll(); } }); } $(document).on('click', '#chat-admin-finalize-button', function(event) { event.preventDefault(); var $form = $('#chat-admin-finalize-form'); showConfirmModal( '¿Deseas finalizar esta conversación? Después de finalizarla ya no podrán enviarse nuevos mensajes.', 'Confirmación', 'Finalizar' ).then(function(confirmado) { if (!confirmado || !$form.length) { return; } $form[0].submit(); }); }); $('#chat-admin-file-input').on('change', function() { updateSelectedFile(); }); $('#chat-admin-message-input').on('input', function() { updateTextareaHeight(); }); $('#chat-admin-reply-form').on('submit', function(event) { event.preventDefault(); if (sendInFlight) { return; } var $form = $(this); var messageValue = $.trim($('#chat-admin-message-input').val()); var fileInput = $('#chat-admin-file-input').get(0); var hasFile = fileInput && fileInput.files && fileInput.files.length > 0; if (messageValue === '' && !hasFile) { showFeedback('danger', 'Captura un mensaje o adjunta un archivo.'); return; } hideFeedback(); sendInFlight = true; $('#chat-admin-send-button').addClass('loading disabled'); $.ajax({ url: $form.attr('action'), type: 'POST', data: new FormData($form.get(0)), processData: false, contentType: false, headers: { 'X-Requested-With': 'XMLHttpRequest' }, success: function(response) { showFeedback('success', response.message || 'Mensaje enviado correctamente.'); if (response.chat_activo) { updateConversationHeader(response.chat_activo); } if (response.mis_chats_html) { updateSidebar(response.mis_chats_html); } if (typeof refreshSystemNotifications === 'function') { refreshSystemNotifications(); } updateReplyAvailability(response.puede_responder); $('#chat-admin-message-input').val(''); $('#chat-admin-file-input').val(''); resetSelectedFile(); updateTextareaHeight(); if (typeof response.historial_html === 'string') { replaceHistoryHtml(response.historial_html, true, false, response.ultimo_id_mensaje); } else { reloadConversationHistory(getActiveChatId(), true, false, response.ultimo_id_mensaje); } }, error: function(xhr) { showFeedback('danger', resolveErrorMessage(xhr)); }, complete: function() { sendInFlight = false; $('#chat-admin-send-button').removeClass('loading disabled'); scheduleChatPoll(); } }); }); $('a[data-toggle="tab"]').on('shown.bs.tab', function(event) { var target = $(event.target).attr('href'); if (target === '#tab-mis-chats') { scrollHistoryToBottom(); scheduleChatPoll(); return; } clearTimeout(pollTimer); }); updateTextareaHeight(); updateSelectedFile(); sortHistoryRows(); if ($('#tab-mis-chats').hasClass('active')) { scrollHistoryToBottom(); scheduleChatPoll(); } } @endsection