167 lines
12 KiB
PHP
167 lines
12 KiB
PHP
<div class="flex h-[calc(100vh-72px)] overflow-hidden bg-gray-100">
|
|
|
|
{{-- Panel izquierdo: lista de contactos --}}
|
|
<div class="w-80 flex-shrink-0 bg-white border-r border-gray-200 flex flex-col">
|
|
{{-- Header --}}
|
|
<div class="p-4 border-b border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 flex items-center gap-2">
|
|
<svg class="w-6 h-6 text-green-500" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347z"/>
|
|
<path d="M12 0C5.373 0 0 5.373 0 12c0 2.127.558 4.122 1.532 5.857L.057 23.786a.5.5 0 0 0 .629.628l5.963-1.467A11.944 11.944 0 0 0 12 24c6.627 0 12-5.373 12-12S18.627 0 12 0zm0 22c-1.88 0-3.638-.52-5.145-1.42l-.369-.22-3.818.94.974-3.782-.239-.381A9.944 9.944 0 0 1 2 12c0-5.523 4.477-10 10-10s10 4.477 10 10-4.477 10-10 10z"/>
|
|
</svg>
|
|
WhatsApp
|
|
</h2>
|
|
<div class="mt-2">
|
|
<input wire:model.live.debounce.300ms="search"
|
|
type="text"
|
|
placeholder="Buscar contacto..."
|
|
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-green-400">
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Lista de usuarios --}}
|
|
<div class="flex-1 overflow-y-auto">
|
|
@forelse($users as $user)
|
|
<button wire:click="selectUser({{ $user->id }})"
|
|
class="w-full text-left px-4 py-3 border-b border-gray-100 hover:bg-gray-50 flex items-center gap-3 transition
|
|
{{ $selectedUserId === $user->id ? 'bg-green-50 border-l-4 border-l-green-500' : '' }}">
|
|
<div class="relative flex-shrink-0">
|
|
<img src="https://ui-avatars.com/api/?name={{ urlencode($user->name ?? $user->phone_number) }}&size=40&background=22c55e&color=fff"
|
|
class="w-10 h-10 rounded-full" alt="">
|
|
<span class="absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white
|
|
{{ $user->status === 'active' ? 'bg-green-400' : ($user->status === 'blocked' ? 'bg-red-400' : 'bg-gray-400') }}">
|
|
</span>
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<div class="flex justify-between items-center">
|
|
<span class="text-sm font-semibold text-gray-800 truncate">{{ $user->name ?? $user->phone_number }}</span>
|
|
@if($user->unread_count > 0)
|
|
<span class="bg-green-500 text-white text-xs rounded-full px-2 py-0.5 ml-1">{{ $user->unread_count }}</span>
|
|
@endif
|
|
</div>
|
|
<p class="text-xs text-gray-500 truncate">
|
|
{{ $user->phone_number }}
|
|
</p>
|
|
@if($user->lastMessage)
|
|
<p class="text-xs text-gray-400 truncate">
|
|
{{ Str::limit($user->lastMessage->content, 40) }}
|
|
</p>
|
|
@endif
|
|
</div>
|
|
</button>
|
|
@empty
|
|
<div class="p-6 text-center text-gray-400 text-sm">No hay contactos</div>
|
|
@endforelse
|
|
</div>
|
|
|
|
{{-- Paginación --}}
|
|
<div class="p-2 border-t border-gray-200 text-xs">
|
|
{{ $users->links() }}
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Panel derecho: chat --}}
|
|
<div class="flex-1 flex flex-col">
|
|
@if($selectedUser)
|
|
{{-- Chat header --}}
|
|
<div class="bg-white px-4 py-3 border-b border-gray-200 flex items-center justify-between shadow-sm">
|
|
<div class="flex items-center gap-3">
|
|
<img src="https://ui-avatars.com/api/?name={{ urlencode($selectedUser->name ?? $selectedUser->phone_number) }}&size=40&background=22c55e&color=fff"
|
|
class="w-10 h-10 rounded-full" alt="">
|
|
<div>
|
|
<div class="font-semibold text-gray-800">{{ $selectedUser->name ?? 'Sin nombre' }}</div>
|
|
<div class="text-xs text-gray-500">{{ $selectedUser->phone_number }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
@if($selectedUser->in_service)
|
|
<span class="text-xs bg-blue-100 text-blue-700 px-2 py-1 rounded-full">En atención</span>
|
|
@endif
|
|
@if($selectedUser->on_hold)
|
|
<span class="text-xs bg-yellow-100 text-yellow-700 px-2 py-1 rounded-full">En espera</span>
|
|
@endif
|
|
<button wire:click="toggleBlockUser"
|
|
class="text-xs px-3 py-1 rounded-full border transition
|
|
{{ $selectedUser->status === 'blocked'
|
|
? 'bg-red-100 text-red-700 border-red-200 hover:bg-red-200'
|
|
: 'bg-gray-100 text-gray-600 border-gray-200 hover:bg-gray-200' }}">
|
|
{{ $selectedUser->status === 'blocked' ? 'Desbloquear' : 'Bloquear' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Mensajes --}}
|
|
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-2 bg-[#e5ddd5]"
|
|
x-data x-init="$nextTick(() => { let el = document.getElementById('chat-messages'); el.scrollTop = el.scrollHeight; })"
|
|
@scroll-to-bottom.window="$nextTick(() => { $el.scrollTop = $el.scrollHeight })">
|
|
|
|
@forelse($messages as $msg)
|
|
<div class="flex {{ $msg->direction === 'outgoing' ? 'justify-end' : 'justify-start' }}">
|
|
<div class="max-w-xs lg:max-w-md xl:max-w-lg {{ $msg->direction === 'outgoing' ? 'bg-[#dcf8c6]' : 'bg-white' }} rounded-lg px-3 py-2 shadow-sm">
|
|
@if($msg->message_type === 'text')
|
|
<p class="text-sm text-gray-800 whitespace-pre-wrap">{{ $msg->content }}</p>
|
|
@elseif(in_array($msg->message_type, ['image', 'video', 'document', 'audio']))
|
|
@if($msg->media_url)
|
|
@if($msg->message_type === 'image')
|
|
<img src="{{ $msg->media_url }}" class="rounded max-w-full" alt="Imagen">
|
|
@elseif($msg->message_type === 'document')
|
|
<a href="{{ $msg->media_url }}" target="_blank" class="text-blue-600 text-sm underline flex items-center gap-1">
|
|
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0-3-3m3 3 3-3M3 17V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"/></svg>
|
|
{{ $msg->filename ?? 'Documento' }}
|
|
</a>
|
|
@else
|
|
<span class="text-xs text-gray-500 italic">[{{ strtoupper($msg->message_type) }}]</span>
|
|
@endif
|
|
@else
|
|
<span class="text-xs text-gray-400 italic">[{{ strtoupper($msg->message_type) }} sin URL]</span>
|
|
@endif
|
|
@else
|
|
<p class="text-sm text-gray-600 italic">{{ $msg->content }}</p>
|
|
@endif
|
|
<div class="text-right mt-1">
|
|
<span class="text-[10px] text-gray-400">{{ $msg->created_at->format('H:i') }}</span>
|
|
@if($msg->direction === 'outgoing')
|
|
<span class="text-[10px] ml-1 {{ $msg->status === 'read' ? 'text-blue-500' : 'text-gray-400' }}">✓✓</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="text-center text-gray-400 text-sm py-10">No hay mensajes</div>
|
|
@endforelse
|
|
</div>
|
|
|
|
{{-- Input de respuesta --}}
|
|
<div class="bg-white border-t border-gray-200 p-3">
|
|
@if($selectedUser->status === 'blocked')
|
|
<div class="text-center text-red-500 text-sm py-2">Usuario bloqueado — desbloquea para responder</div>
|
|
@else
|
|
<form wire:submit.prevent="sendReply" class="flex items-end gap-2">
|
|
<textarea wire:model="replyText"
|
|
placeholder="Escribe un mensaje..."
|
|
rows="2"
|
|
class="flex-1 border border-gray-300 rounded-lg px-3 py-2 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-green-400"
|
|
x-on:keydown.ctrl.enter="$wire.sendReply()"></textarea>
|
|
<button type="submit"
|
|
class="bg-green-500 hover:bg-green-600 text-white p-3 rounded-lg transition">
|
|
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>
|
|
</svg>
|
|
</button>
|
|
</form>
|
|
@error('replyText') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
|
|
@if(session('error')) <p class="text-red-500 text-xs mt-1">{{ session('error') }}</p> @endif
|
|
@endif
|
|
</div>
|
|
@else
|
|
<div class="flex-1 flex flex-col items-center justify-center text-gray-400 bg-[#e5ddd5]">
|
|
<svg class="w-20 h-20 mb-4 opacity-30" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347z"/>
|
|
</svg>
|
|
<p class="text-lg font-medium">Selecciona una conversación</p>
|
|
<p class="text-sm">Elige un contacto de la lista para ver el chat</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|