This commit is contained in:
Lizandro Guarnizo
2026-04-24 21:40:10 -05:00
parent 877a0d393a
commit 148fa6b9f9
36 changed files with 3037 additions and 0 deletions
@@ -0,0 +1,118 @@
<div>
<div class="max-w-3xl mx-auto">
<h1 class="text-2xl font-bold text-gray-800 flex items-center gap-2 mb-6">
<svg class="w-7 h-7 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
Configuración del Bot WhatsApp
</h1>
<form wire:submit.prevent="save" class="space-y-6">
{{-- Credenciales Meta --}}
<div class="bg-white rounded-xl shadow p-6 space-y-4">
<h2 class="text-base font-semibold text-gray-700 border-b border-gray-100 pb-2">Credenciales Meta / Graph API</h2>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Token de acceso (Bearer) *</label>
<input wire:model="whatsapp_token" type="password"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400 font-mono">
@error('whatsapp_token') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Phone Number ID *</label>
<input wire:model="phone_number_id" type="text" placeholder="123456789012345"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400 font-mono">
@error('phone_number_id') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">URL API (Graph) *</label>
<input wire:model="whatsapp_api_url" type="url"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400 font-mono">
@error('whatsapp_api_url') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Webhook Verify Token *</label>
<input wire:model="webhook_verify_token" type="text"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400 font-mono">
@error('webhook_verify_token') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
</div>
{{-- Control del bot --}}
<div class="bg-white rounded-xl shadow p-6 space-y-4">
<h2 class="text-base font-semibold text-gray-700 border-b border-gray-100 pb-2">Control del Bot</h2>
<div class="flex items-center gap-4">
<label class="text-sm font-medium text-gray-700">Estado del bot</label>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" wire:model="bot_enabled" value="1"
class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-500"></div>
<span class="ml-3 text-sm text-gray-600">{{ $bot_enabled ? 'Activo' : 'Inactivo' }}</span>
</label>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Mensaje de bienvenida *</label>
<textarea wire:model="welcome_message" rows="3"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-green-400"></textarea>
@error('welcome_message') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Respuesta cuando no entiende *</label>
<textarea wire:model="default_no_match" rows="2"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-green-400"></textarea>
@error('default_no_match') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Mensaje al solicitar asesor *</label>
<textarea wire:model="advisor_message" rows="2"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-green-400"></textarea>
@error('advisor_message') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
</div>
{{-- Horarios --}}
<div class="bg-white rounded-xl shadow p-6 space-y-4">
<h2 class="text-base font-semibold text-gray-700 border-b border-gray-100 pb-2">Horarios de Atención</h2>
<div class="flex items-center gap-4">
<label class="text-sm font-medium text-gray-700">Validar horario</label>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" wire:model="business_hours_enabled" value="1" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-500"></div>
</label>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Hora inicio</label>
<input wire:model="business_hours_start" type="time"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Hora fin</label>
<input wire:model="business_hours_end" type="time"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
</div>
</div>
</div>
<div class="flex justify-end">
<button type="submit"
class="bg-green-500 hover:bg-green-600 text-white px-6 py-3 rounded-lg font-semibold text-sm transition flex items-center gap-2">
<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="M5 13l4 4L19 7"/></svg>
Guardar Configuración
</button>
</div>
</form>
</div>
</div>
@@ -0,0 +1,166 @@
<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>
@@ -0,0 +1,82 @@
<div>
<div class="flex items-center justify-between mb-4">
<h1 class="text-2xl font-bold text-gray-800 flex items-center gap-2">
<svg class="w-7 h-7 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
</svg>
Logs del Webhook
</h1>
<button wire:click="clearAll" wire:confirm="¿Eliminar TODOS los logs? Esta acción no se puede deshacer."
class="bg-red-500 hover:bg-red-600 text-white text-sm px-4 py-2 rounded-lg flex items-center gap-2 transition">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
Limpiar logs
</button>
</div>
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Buscar en el payload..."
class="mb-4 border border-gray-300 rounded-lg px-4 py-2 text-sm w-64 focus:outline-none focus:ring-2 focus:ring-green-400">
<div class="bg-white rounded-xl shadow overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b">
<tr>
<th class="px-4 py-3 text-left text-gray-600">ID</th>
<th class="px-4 py-3 text-left text-gray-600">Fecha</th>
<th class="px-4 py-3 text-center text-gray-600">HTTP</th>
<th class="px-4 py-3 text-left text-gray-600">Payload (preview)</th>
<th class="px-4 py-3 text-center text-gray-600">Acciones</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse($logs as $log)
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 text-gray-400 text-xs">{{ $log->id }}</td>
<td class="px-4 py-3 text-gray-600 text-xs">{{ $log->created_at?->format('d/m/Y H:i:s') }}</td>
<td class="px-4 py-3 text-center">
@if($log->status_code)
<span class="text-xs px-2 py-0.5 rounded-full {{ $log->status_code < 300 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-600' }}">
{{ $log->status_code }}
</span>
@else
<span class="text-gray-400"></span>
@endif
</td>
<td class="px-4 py-3 font-mono text-xs text-gray-500 max-w-md truncate">
{{ Str::limit($log->payload, 120) }}
</td>
<td class="px-4 py-3">
<div class="flex justify-center gap-2">
<button wire:click="viewLog({{ $log->id }})"
class="text-blue-500 hover:text-blue-700 transition" title="Ver detalle">
<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="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
</button>
<button wire:click="deleteLog({{ $log->id }})"
class="text-red-400 hover:text-red-600 transition" title="Eliminar">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
</button>
</div>
</td>
</tr>
@empty
<tr><td colspan="5" class="px-4 py-8 text-center text-gray-400">No hay logs registrados</td></tr>
@endforelse
</tbody>
</table>
<div class="p-3 border-t border-gray-100">{{ $logs->links() }}</div>
</div>
{{-- Modal detalle payload --}}
@if($viewId)
<div class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-2xl shadow-xl w-full max-w-3xl p-6 max-h-[80vh] flex flex-col">
<div class="flex justify-between items-center mb-4">
<h3 class="font-bold text-gray-800">Log #{{ $viewId }} — Payload completo</h3>
<button wire:click="clearView" class="text-gray-400 hover:text-gray-700">
<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="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
<pre class="overflow-auto flex-1 bg-gray-900 text-green-400 text-xs rounded-lg p-4 font-mono">{{ $viewPayload }}</pre>
</div>
</div>
@endif
</div>
@@ -0,0 +1,250 @@
<div>
<div class="flex items-center justify-between mb-4">
<h1 class="text-2xl font-bold text-gray-800 flex items-center gap-2">
<svg class="w-7 h-7 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h10"/>
</svg>
Menús del Bot
</h1>
<button wire:click="openMenuForm()"
class="bg-green-500 hover:bg-green-600 text-white text-sm px-4 py-2 rounded-lg flex items-center gap-2 transition">
<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 4v16m8-8H4"/></svg>
Nuevo Menú
</button>
</div>
{{-- Buscador --}}
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Buscar menú..."
class="mb-4 border border-gray-300 rounded-lg px-4 py-2 text-sm w-full max-w-xs focus:outline-none focus:ring-2 focus:ring-green-400">
{{-- Tabla de menús --}}
<div class="bg-white rounded-xl shadow overflow-hidden mb-6">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="px-4 py-3 text-left text-gray-600">Nombre</th>
<th class="px-4 py-3 text-left text-gray-600">Título</th>
<th class="px-4 py-3 text-center text-gray-600">Principal</th>
<th class="px-4 py-3 text-center text-gray-600">Activo</th>
<th class="px-4 py-3 text-center text-gray-600">Opciones</th>
<th class="px-4 py-3 text-center text-gray-600">Acciones</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse($menus as $menu)
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 font-medium text-gray-800">{{ $menu->name }}</td>
<td class="px-4 py-3 text-gray-600">{{ $menu->title }}</td>
<td class="px-4 py-3 text-center">
@if($menu->is_main)
<span class="bg-green-100 text-green-700 text-xs px-2 py-0.5 rounded-full"></span>
@else
<span class="text-gray-400"></span>
@endif
</td>
<td class="px-4 py-3 text-center">
<span class="text-xs px-2 py-0.5 rounded-full {{ $menu->is_active ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-600' }}">
{{ $menu->is_active ? 'Activo' : 'Inactivo' }}
</span>
</td>
<td class="px-4 py-3 text-center">
<button wire:click="$set('viewMenuId', {{ $menu->id }})"
class="text-blue-500 hover:underline text-xs">
{{ $menu->options_count }} opcs.
</button>
</td>
<td class="px-4 py-3">
<div class="flex justify-center gap-2">
<button wire:click="openMenuForm({{ $menu->id }})"
class="text-yellow-500 hover:text-yellow-700 transition" title="Editar">
<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="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
</button>
<button wire:click="openOptionForm(null, {{ $menu->id }})"
class="text-green-500 hover:text-green-700 transition" title="Agregar opción">
<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 4v16m8-8H4"/></svg>
</button>
<button wire:click="deleteMenu({{ $menu->id }})"
wire:confirm="¿Eliminar este menú y todas sus opciones?"
class="text-red-400 hover:text-red-600 transition" title="Eliminar">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
</button>
</div>
</td>
</tr>
@empty
<tr><td colspan="6" class="px-4 py-8 text-center text-gray-400">No hay menús creados</td></tr>
@endforelse
</tbody>
</table>
</div>
{{-- Detalle opciones del menú seleccionado --}}
@if($viewMenu)
<div class="bg-white rounded-xl shadow p-4 mb-6">
<div class="flex items-center justify-between mb-3">
<h3 class="font-bold text-gray-800">Opciones del menú: <span class="text-green-600">{{ $viewMenu->name }}</span></h3>
<div class="flex gap-2">
<button wire:click="openOptionForm(null, {{ $viewMenu->id }})"
class="text-sm bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded-lg 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 4v16m8-8H4"/></svg>
Agregar opción
</button>
<button wire:click="$set('viewMenuId', null)" class="text-sm text-gray-500 hover:text-gray-700">Cerrar</button>
</div>
</div>
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b">
<tr>
<th class="px-3 py-2 text-left text-gray-600">#</th>
<th class="px-3 py-2 text-left text-gray-600">Título</th>
<th class="px-3 py-2 text-left text-gray-600">Acción</th>
<th class="px-3 py-2 text-left text-gray-600">Valor</th>
<th class="px-3 py-2 text-center text-gray-600">Activa</th>
<th class="px-3 py-2 text-center text-gray-600">Ops</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse($viewMenu->options as $opt)
<tr>
<td class="px-3 py-2 font-bold text-gray-700">{{ $opt->option_number }}</td>
<td class="px-3 py-2">{{ $opt->title }}</td>
<td class="px-3 py-2">
<span class="bg-blue-50 text-blue-700 text-xs px-2 py-0.5 rounded">{{ $opt->action_type }}</span>
</td>
<td class="px-3 py-2 text-gray-500 max-w-xs truncate">{{ $opt->action_value }}</td>
<td class="px-3 py-2 text-center">
<span class="text-xs {{ $opt->is_active ? 'text-green-600' : 'text-red-400' }}">{{ $opt->is_active ? '✓' : '✗' }}</span>
</td>
<td class="px-3 py-2">
<div class="flex justify-center gap-2">
<button wire:click="openOptionForm({{ $opt->id }})" class="text-yellow-500 hover:text-yellow-700">
<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="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
</button>
<button wire:click="deleteOption({{ $opt->id }})" wire:confirm="¿Eliminar esta opción?"
class="text-red-400 hover:text-red-600">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
</button>
</div>
</td>
</tr>
@empty
<tr><td colspan="6" class="px-3 py-4 text-center text-gray-400">Sin opciones</td></tr>
@endforelse
</tbody>
</table>
</div>
@endif
{{-- Modal: Formulario Menú --}}
@if($showMenuForm)
<div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-2xl shadow-xl w-full max-w-lg p-6">
<h3 class="text-lg font-bold text-gray-800 mb-4">{{ $editMenuId ? 'Editar Menú' : 'Nuevo Menú' }}</h3>
<form wire:submit.prevent="saveMenu" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Nombre interno *</label>
<input wire:model="menuName" type="text" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('menuName') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Título visible *</label>
<input wire:model="menuTitle" type="text" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('menuTitle') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Mensaje del menú *</label>
<textarea wire:model="menuMessage" rows="4"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400 resize-none"></textarea>
@error('menuMessage') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Menú padre</label>
<select wire:model="menuParentId" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
<option value=""> Ninguno </option>
@foreach($allMenus as $m)
@if($m->id !== $editMenuId)
<option value="{{ $m->id }}">{{ $m->name }}</option>
@endif
@endforeach
</select>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Orden</label>
<input wire:model="menuSortOrder" type="number" min="0"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
</div>
</div>
<div class="flex gap-4">
<label class="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
<input wire:model="menuIsMain" type="checkbox" class="rounded"> Es menú principal
</label>
<label class="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
<input wire:model="menuIsActive" type="checkbox" class="rounded"> Activo
</label>
</div>
<div class="flex justify-end gap-3 pt-2 border-t border-gray-100">
<button type="button" wire:click="resetMenuForm()" class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800">Cancelar</button>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white text-sm px-4 py-2 rounded-lg">Guardar</button>
</div>
</form>
</div>
</div>
@endif
{{-- Modal: Formulario Opción --}}
@if($showOptionForm)
<div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-2xl shadow-xl w-full max-w-lg p-6">
<h3 class="text-lg font-bold text-gray-800 mb-4">{{ $editOptionId ? 'Editar Opción' : 'Nueva Opción' }}</h3>
<form wire:submit.prevent="saveOption" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Número de opción *</label>
<input wire:model="optionNumber" type="number" min="1"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Título *</label>
<input wire:model="optionTitle" type="text"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('optionTitle') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Tipo de acción</label>
<select wire:model="optionAction" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
<option value="message">Mensaje</option>
<option value="submenu">Submenú</option>
<option value="template">Plantilla</option>
<option value="url">URL</option>
<option value="advisor">Asesor</option>
<option value="flow">Flujo</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Orden</label>
<input wire:model="optionSort" type="number" min="0"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
</div>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Valor de la acción</label>
<input wire:model="optionValue" type="text" placeholder="ID de submenú, texto, URL..."
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
</div>
<label class="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
<input wire:model="optionIsActive" type="checkbox" class="rounded"> Opción activa
</label>
<div class="flex justify-end gap-3 pt-2 border-t border-gray-100">
<button type="button" wire:click="resetOptionForm()" class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800">Cancelar</button>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white text-sm px-4 py-2 rounded-lg">Guardar</button>
</div>
</form>
</div>
</div>
@endif
</div>
@@ -0,0 +1,141 @@
<div>
<div class="flex items-center justify-between mb-4">
<h1 class="text-2xl font-bold text-gray-800 flex items-center gap-2">
<svg class="w-7 h-7 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
Plantillas de Mensaje
</h1>
<button wire:click="openForm()" class="bg-green-500 hover:bg-green-600 text-white text-sm px-4 py-2 rounded-lg flex items-center gap-2 transition">
<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 4v16m8-8H4"/></svg>
Nueva Plantilla
</button>
</div>
<div class="flex gap-3 mb-4 flex-wrap">
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Buscar plantilla..."
class="border border-gray-300 rounded-lg px-4 py-2 text-sm w-64 focus:outline-none focus:ring-2 focus:ring-green-400">
<select wire:model.live="filterStatus" class="border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none">
<option value=""> Todos los estados </option>
<option value="pending">Pendiente</option>
<option value="approved">Aprobada</option>
<option value="rejected">Rechazada</option>
<option value="paused">Pausada</option>
</select>
</div>
<div class="bg-white rounded-xl shadow overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b">
<tr>
<th class="px-4 py-3 text-left text-gray-600">Nombre</th>
<th class="px-4 py-3 text-left text-gray-600">Template (Meta)</th>
<th class="px-4 py-3 text-left text-gray-600">Idioma</th>
<th class="px-4 py-3 text-left text-gray-600">Header</th>
<th class="px-4 py-3 text-center text-gray-600">Estado</th>
<th class="px-4 py-3 text-center text-gray-600">Acciones</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse($templates as $tpl)
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 font-medium text-gray-800">{{ $tpl->name }}</td>
<td class="px-4 py-3 font-mono text-xs text-gray-600">{{ $tpl->template_name }}</td>
<td class="px-4 py-3 uppercase text-gray-500 text-xs">{{ $tpl->language_code }}</td>
<td class="px-4 py-3 text-gray-500 text-xs capitalize">{{ $tpl->header_type }}</td>
<td class="px-4 py-3 text-center">
@php
$colors = ['approved'=>'green','pending'=>'yellow','rejected'=>'red','paused'=>'gray'];
$c = $colors[$tpl->status] ?? 'gray';
@endphp
<span class="text-xs px-2 py-0.5 rounded-full bg-{{ $c }}-100 text-{{ $c }}-700 capitalize">{{ $tpl->status }}</span>
</td>
<td class="px-4 py-3">
<div class="flex justify-center gap-2">
<button wire:click="openForm({{ $tpl->id }})" class="text-yellow-500 hover:text-yellow-700 transition">
<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="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
</button>
<button wire:click="delete({{ $tpl->id }})" wire:confirm="¿Eliminar esta plantilla?" class="text-red-400 hover:text-red-600 transition">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
</button>
</div>
</td>
</tr>
@empty
<tr><td colspan="6" class="px-4 py-8 text-center text-gray-400">No hay plantillas registradas</td></tr>
@endforelse
</tbody>
</table>
<div class="p-3 border-t border-gray-100">{{ $templates->links() }}</div>
</div>
{{-- Modal Formulario --}}
@if($showForm)
<div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-2xl shadow-xl w-full max-w-lg p-6 max-h-screen overflow-y-auto">
<h3 class="text-lg font-bold text-gray-800 mb-4">{{ $editId ? 'Editar Plantilla' : 'Nueva Plantilla' }}</h3>
<form wire:submit.prevent="save" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Nombre interno *</label>
<input wire:model="name" type="text" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('name') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Nombre en Meta *</label>
<input wire:model="templateName" type="text" placeholder="solo_minusculas_sin_espacios"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('templateName') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
</div>
<div class="grid grid-cols-3 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Idioma</label>
<input wire:model="languageCode" type="text" placeholder="es"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Estado</label>
<select wire:model="status" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
<option value="pending">Pendiente</option>
<option value="approved">Aprobada</option>
<option value="rejected">Rechazada</option>
<option value="paused">Pausada</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Tipo header</label>
<select wire:model="headerType" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
<option value="none">Ninguno</option>
<option value="text">Texto</option>
<option value="image">Imagen</option>
<option value="video">Video</option>
<option value="document">Documento</option>
</select>
</div>
</div>
@if($headerType === 'text')
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Texto del header</label>
<input wire:model="headerText" type="text" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
</div>
@endif
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Cuerpo del mensaje *</label>
<textarea wire:model="bodyText" rows="4" placeholder="Hola {{nombre}}, tu pedido {{codigo}} está listo."
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-green-400"></textarea>
@error('bodyText') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Pie de página</label>
<input wire:model="footerText" type="text" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
</div>
<div class="flex justify-end gap-3 pt-2 border-t border-gray-100">
<button type="button" wire:click="resetForm()" class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800">Cancelar</button>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white text-sm px-4 py-2 rounded-lg">Guardar</button>
</div>
</form>
</div>
</div>
@endif
</div>
@@ -0,0 +1,142 @@
<div>
<div class="flex items-center justify-between mb-4">
<h1 class="text-2xl font-bold text-gray-800 flex items-center gap-2">
<svg class="w-7 h-7 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
Mensajes Programados
</h1>
<button wire:click="$set('showForm', true)"
class="bg-green-500 hover:bg-green-600 text-white text-sm px-4 py-2 rounded-lg flex items-center gap-2 transition">
<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 4v16m8-8H4"/></svg>
Programar Mensaje
</button>
</div>
<div class="flex gap-3 mb-4 flex-wrap">
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Buscar por teléfono..."
class="border border-gray-300 rounded-lg px-4 py-2 text-sm w-64 focus:outline-none focus:ring-2 focus:ring-green-400">
<select wire:model.live="filterStatus" class="border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none">
<option value=""> Todos </option>
<option value="pending">Pendiente</option>
<option value="sent">Enviado</option>
<option value="failed">Fallido</option>
<option value="cancelled">Cancelado</option>
</select>
</div>
<div class="bg-white rounded-xl shadow overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b">
<tr>
<th class="px-4 py-3 text-left text-gray-600">Teléfono</th>
<th class="px-4 py-3 text-left text-gray-600">Tipo</th>
<th class="px-4 py-3 text-left text-gray-600">Contenido / Plantilla</th>
<th class="px-4 py-3 text-left text-gray-600">Fecha programada</th>
<th class="px-4 py-3 text-center text-gray-600">Estado</th>
<th class="px-4 py-3 text-center text-gray-600">Acciones</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse($messages as $msg)
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 font-mono text-xs">{{ $msg->phone_number }}</td>
<td class="px-4 py-3">
<span class="text-xs bg-blue-50 text-blue-700 px-2 py-0.5 rounded uppercase">{{ $msg->message_type }}</span>
</td>
<td class="px-4 py-3 text-gray-600 max-w-xs truncate">
{{ $msg->message_type === 'text' ? Str::limit($msg->message_content, 50) : $msg->template_name }}
</td>
<td class="px-4 py-3 text-gray-600">
{{ \Carbon\Carbon::parse($msg->scheduled_date)->format('d/m/Y') }}
{{ $msg->scheduled_time }}
</td>
<td class="px-4 py-3 text-center">
@php
$colors = ['pending'=>'yellow','sent'=>'green','failed'=>'red','cancelled'=>'gray'];
$c = $colors[$msg->status] ?? 'gray';
@endphp
<span class="text-xs px-2 py-0.5 rounded-full bg-{{ $c }}-100 text-{{ $c }}-700 capitalize">{{ $msg->status }}</span>
</td>
<td class="px-4 py-3">
<div class="flex justify-center gap-2">
@if($msg->status === 'pending')
<button wire:click="cancel({{ $msg->id }})" wire:confirm="¿Cancelar este mensaje?"
class="text-yellow-500 hover:text-yellow-700 text-xs">Cancelar</button>
@endif
<button wire:click="delete({{ $msg->id }})" wire:confirm="¿Eliminar este registro?"
class="text-red-400 hover:text-red-600 transition">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
</button>
</div>
</td>
</tr>
@empty
<tr><td colspan="6" class="px-4 py-8 text-center text-gray-400">No hay mensajes programados</td></tr>
@endforelse
</tbody>
</table>
<div class="p-3 border-t border-gray-100">{{ $messages->links() }}</div>
</div>
{{-- Modal formulario --}}
@if($showForm)
<div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-2xl shadow-xl w-full max-w-md p-6">
<h3 class="text-lg font-bold text-gray-800 mb-4">Programar Mensaje</h3>
<form wire:submit.prevent="save" class="space-y-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Número de teléfono *</label>
<input wire:model="phoneNumber" type="text" placeholder="573001234567"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('phoneNumber') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Fecha *</label>
<input wire:model="scheduledDate" type="date"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('scheduledDate') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Hora *</label>
<input wire:model="scheduledTime" type="time"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-400">
@error('scheduledTime') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Tipo de mensaje</label>
<select wire:model.live="messageType" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
<option value="text">Texto libre</option>
<option value="template">Plantilla</option>
</select>
</div>
@if($messageType === 'text')
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Mensaje *</label>
<textarea wire:model="messageContent" rows="3"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-green-400"></textarea>
@error('messageContent') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
@else
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">Plantilla *</label>
<select wire:model="templateId" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none">
<option value=""> Seleccionar </option>
@foreach($templates as $tpl)
<option value="{{ $tpl->id }}">{{ $tpl->name }} ({{ $tpl->template_name }})</option>
@endforeach
</select>
@error('templateId') <p class="text-red-500 text-xs">{{ $message }}</p> @enderror
</div>
@endif
<div class="flex justify-end gap-3 pt-2 border-t border-gray-100">
<button type="button" wire:click="resetForm()" class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800">Cancelar</button>
<button type="submit" class="bg-green-500 hover:bg-green-600 text-white text-sm px-4 py-2 rounded-lg">Programar</button>
</div>
</form>
</div>
</div>
@endif
</div>