282 lines
12 KiB
PHP
282 lines
12 KiB
PHP
@extends('layouts.landing')
|
|
|
|
@section('title', 'Chat WhatsApp')
|
|
|
|
@section('content')
|
|
<div class="container-fluid">
|
|
<div class="row" style="height:calc(100vh - 180px)">
|
|
|
|
{{-- Panel izquierdo: lista de prospectos --}}
|
|
<div class="col-md-4 col-lg-3 pr-md-1 mb-3 mb-md-0">
|
|
<div class="card shadow h-100 d-flex flex-column">
|
|
<div class="card-header py-2 d-flex align-items-center justify-content-between">
|
|
<h6 class="m-0 font-weight-bold text-primary">
|
|
<i class="fab fa-whatsapp mr-1"></i> Conversaciones
|
|
</h6>
|
|
<a href="{{ route('promocion.index') }}" class="btn btn-sm btn-secondary">
|
|
<i class="fas fa-arrow-left"></i>
|
|
</a>
|
|
</div>
|
|
<div class="card-body p-0" style="overflow-y:auto; flex:1">
|
|
@forelse($prospectos as $item)
|
|
@php
|
|
$alumno = $item['alumno'];
|
|
$user = $item['user'];
|
|
$ultimo = $item['ultimo'];
|
|
@endphp
|
|
<a href="#"
|
|
class="prospecto-item d-flex align-items-center p-3 border-bottom text-dark text-decoration-none"
|
|
data-id="{{ $alumno->id }}"
|
|
data-nombre="{{ $user ? $user->name.' '.$user->apellidoPaterno : 'Alumno #'.$alumno->id }}"
|
|
data-avatar="{{ $user?->profile_photo_url ?? '' }}">
|
|
<div class="position-relative flex-shrink-0 mr-3">
|
|
@if($user && $user->profile_photo_url)
|
|
<img src="{{ $user->profile_photo_url }}"
|
|
class="rounded-circle" width="40" height="40"
|
|
style="object-fit:cover" alt="">
|
|
@else
|
|
<div class="rounded-circle bg-secondary d-flex align-items-center justify-content-center"
|
|
style="width:40px;height:40px">
|
|
<i class="fas fa-user text-white small"></i>
|
|
</div>
|
|
@endif
|
|
@if($item['no_leidos'] > 0)
|
|
<span class="badge badge-danger badge-counter position-absolute"
|
|
style="top:-4px;right:-6px;min-width:18px"
|
|
id="badge-{{ $alumno->id }}">
|
|
{{ $item['no_leidos'] }}
|
|
</span>
|
|
@else
|
|
<span class="badge badge-danger badge-counter d-none position-absolute"
|
|
style="top:-4px;right:-6px;min-width:18px"
|
|
id="badge-{{ $alumno->id }}">0</span>
|
|
@endif
|
|
</div>
|
|
<div style="min-width:0;flex:1">
|
|
<div class="font-weight-bold small text-truncate">
|
|
{{ $user ? $user->name.' '.$user->apellidoPaterno : 'Alumno #'.$alumno->id }}
|
|
</div>
|
|
<div class="small text-muted text-truncate">
|
|
{{ $ultimo?->mensaje ?? 'Sin mensajes' }}
|
|
</div>
|
|
</div>
|
|
<div class="small text-muted ml-2 flex-shrink-0">
|
|
{{ $ultimo?->created_at?->format('H:i') ?? '' }}
|
|
</div>
|
|
</a>
|
|
@empty
|
|
<div class="text-center text-muted py-5">
|
|
<i class="fab fa-whatsapp fa-3x mb-2 d-block text-success"></i>
|
|
Sin conversaciones aún.
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Panel derecho: historial + envío --}}
|
|
<div class="col-md-8 col-lg-9 pl-md-1">
|
|
<div class="card shadow h-100 d-flex flex-column">
|
|
{{-- Header del chat --}}
|
|
<div class="card-header py-2 d-flex align-items-center" id="chatHeader" style="display:none!important">
|
|
<div class="rounded-circle bg-secondary d-flex align-items-center justify-content-center mr-3"
|
|
style="width:36px;height:36px;overflow:hidden" id="chatAvatar">
|
|
<i class="fas fa-user text-white small"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-weight-bold" id="chatNombre">—</div>
|
|
<div class="small text-muted" id="chatTel"></div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Estado vacío --}}
|
|
<div class="card-body d-flex align-items-center justify-content-center" id="chatVacio">
|
|
<div class="text-center text-muted">
|
|
<i class="fab fa-whatsapp fa-4x text-success mb-3 d-block"></i>
|
|
Selecciona un prospecto para ver la conversación
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Mensajes --}}
|
|
<div class="card-body p-3 d-none" id="chatMensajes"
|
|
style="flex:1;overflow-y:auto;background:#ece5dd;">
|
|
</div>
|
|
|
|
{{-- Input de envío --}}
|
|
<div class="card-footer p-2 d-none" id="chatFooter">
|
|
<form id="chatForm" class="d-flex">
|
|
<input type="text" id="chatInput" class="form-control form-control-sm mr-2"
|
|
placeholder="Escribe un mensaje..." autocomplete="off">
|
|
<button type="submit" class="btn btn-sm btn-success">
|
|
<i class="fas fa-paper-plane"></i>
|
|
</button>
|
|
</form>
|
|
<div id="chatError" class="small text-danger mt-1 d-none"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@section('scripts')
|
|
<script>
|
|
(function () {
|
|
let alumnoActual = null;
|
|
let echoChannel = null;
|
|
|
|
const $vacio = document.getElementById('chatVacio');
|
|
const $mensajes = document.getElementById('chatMensajes');
|
|
const $footer = document.getElementById('chatFooter');
|
|
const $header = document.getElementById('chatHeader');
|
|
const $nombre = document.getElementById('chatNombre');
|
|
const $tel = document.getElementById('chatTel');
|
|
const $avatar = document.getElementById('chatAvatar');
|
|
const $input = document.getElementById('chatInput');
|
|
const $error = document.getElementById('chatError');
|
|
const $form = document.getElementById('chatForm');
|
|
|
|
// Seleccionar prospecto
|
|
document.querySelectorAll('.prospecto-item').forEach(el => {
|
|
el.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
document.querySelectorAll('.prospecto-item').forEach(x => x.classList.remove('bg-light'));
|
|
this.classList.add('bg-light');
|
|
|
|
alumnoActual = this.dataset.id;
|
|
const nombre = this.dataset.nombre;
|
|
const avatar = this.dataset.avatar;
|
|
|
|
$nombre.textContent = nombre;
|
|
|
|
if (avatar) {
|
|
$avatar.innerHTML = `<img src="${avatar}" class="rounded-circle" style="width:36px;height:36px;object-fit:cover" alt="">`;
|
|
} else {
|
|
$avatar.innerHTML = '<i class="fas fa-user text-white small"></i>';
|
|
}
|
|
|
|
$header.style.removeProperty('display');
|
|
$vacio.classList.add('d-none');
|
|
$mensajes.classList.remove('d-none');
|
|
$footer.classList.remove('d-none');
|
|
$error.classList.add('d-none');
|
|
|
|
cargarMensajes(alumnoActual);
|
|
suscribirCanal(alumnoActual);
|
|
|
|
// Limpiar badge
|
|
const badge = document.getElementById('badge-' + alumnoActual);
|
|
if (badge) badge.classList.add('d-none');
|
|
});
|
|
});
|
|
|
|
function cargarMensajes(id) {
|
|
$mensajes.innerHTML = '<div class="text-center py-3"><i class="fas fa-spinner fa-spin"></i></div>';
|
|
$tel.textContent = '';
|
|
|
|
axios.get(`/promociones/chat/${id}`)
|
|
.then(res => {
|
|
$tel.textContent = res.data.alumno.tel ?? '';
|
|
renderMensajes(res.data.logs);
|
|
})
|
|
.catch(() => {
|
|
$mensajes.innerHTML = '<div class="text-center text-danger py-3">Error al cargar mensajes.</div>';
|
|
});
|
|
}
|
|
|
|
function renderMensajes(logs) {
|
|
$mensajes.innerHTML = '';
|
|
logs.forEach(log => agregarMensaje(log));
|
|
$mensajes.scrollTop = $mensajes.scrollHeight;
|
|
}
|
|
|
|
function agregarMensaje(log) {
|
|
const enviado = log.tipo === 'enviado';
|
|
const wrapper = document.createElement('div');
|
|
wrapper.className = `d-flex mb-2 ${enviado ? 'justify-content-end' : 'justify-content-start'}`;
|
|
|
|
let contenido = '';
|
|
|
|
if (log.media_type === 'image' && log.mensaje) {
|
|
contenido = `<img src="/storage/${log.mensaje}" class="img-fluid rounded" style="max-width:200px" alt="imagen">`;
|
|
} else if (log.media_type && log.mensaje) {
|
|
const nombre = log.mensaje.split('/').pop();
|
|
contenido = `<a href="/storage/${log.mensaje}" target="_blank" class="btn btn-sm btn-outline-secondary">
|
|
<i class="fas fa-paperclip mr-1"></i>${nombre}
|
|
</a>`;
|
|
} else {
|
|
contenido = escHTML(log.mensaje ?? '');
|
|
}
|
|
|
|
wrapper.innerHTML = `
|
|
<div class="px-3 py-2 rounded shadow-sm" style="max-width:70%;background:${enviado ? '#dcf8c6' : '#fff'};word-break:break-word">
|
|
${contenido}
|
|
<div class="text-right" style="font-size:.7rem;color:#999;margin-top:4px">${log.created_at}</div>
|
|
</div>`;
|
|
|
|
$mensajes.appendChild(wrapper);
|
|
$mensajes.scrollTop = $mensajes.scrollHeight;
|
|
}
|
|
|
|
function suscribirCanal(id) {
|
|
if (echoChannel) {
|
|
window.Echo.leave(echoChannel);
|
|
}
|
|
|
|
echoChannel = `whatsapp-chat.${id}`;
|
|
|
|
window.Echo.private(echoChannel)
|
|
.listen('NuevoMensajeWhatsApp', (e) => {
|
|
if (String(alumnoActual) === String(e.alumno_id)) {
|
|
agregarMensaje({
|
|
tipo: e.tipo,
|
|
mensaje: e.mensaje,
|
|
media_type: e.media_type,
|
|
created_at: e.created_at,
|
|
});
|
|
} else {
|
|
// Actualizar badge del prospecto en la lista
|
|
const badge = document.getElementById('badge-' + e.alumno_id);
|
|
if (badge) {
|
|
const n = (parseInt(badge.textContent) || 0) + 1;
|
|
badge.textContent = n;
|
|
badge.classList.remove('d-none');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
$form.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
const texto = $input.value.trim();
|
|
if (!texto || !alumnoActual) return;
|
|
|
|
$input.disabled = true;
|
|
$error.classList.add('d-none');
|
|
|
|
axios.post('/promociones/chat/send', { alumno_id: alumnoActual, mensaje: texto })
|
|
.then(res => {
|
|
$input.value = '';
|
|
agregarMensaje(res.data);
|
|
})
|
|
.catch(err => {
|
|
const msg = err.response?.data?.error ?? 'Error al enviar el mensaje.';
|
|
$error.textContent = msg;
|
|
$error.classList.remove('d-none');
|
|
})
|
|
.finally(() => {
|
|
$input.disabled = false;
|
|
$input.focus();
|
|
});
|
|
});
|
|
|
|
function escHTML(str) {
|
|
const d = document.createElement('div');
|
|
d.appendChild(document.createTextNode(str));
|
|
return d.innerHTML;
|
|
}
|
|
})();
|
|
</script>
|
|
@endsection
|