feat(whatsapp): mismo motor, menú y configuración que Telegram y el chat web

WhatsApp dejaba de usar su bot propio (árbol de menús sin login ni compras) y
pasa al motor compartido, así los tres canales quedan iguales.

- TelegramBotService recibe el canal por constructor; flujos, menús, Gemini,
  OCR, validación de pagos y datos bancarios salen de ChatConfig sin duplicar
- WhatsApp no tiene teclados inline: las opciones se envían numeradas y la
  respuesta numérica se traduce al callback equivalente
- estado en caché con prefijo por canal (Telegram conserva el suyo para no
  cerrar las sesiones abiertas)
- descarga de comprobantes y audios por la Graph API de Meta
- respuesta manual del asesor y aviso de vencimiento salen también por WhatsApp
- la pantalla de WhatsApp queda sólo con credenciales; el resto se administra
  en Chat / Bot

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-26 23:24:21 -05:00
co-authored by Claude Opus 5
parent e3ad0a423a
commit 5bc1ec14ca
10 changed files with 294 additions and 105 deletions
@@ -66,52 +66,12 @@
</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>
<p class="text-xs text-gray-400">
Los mensajes, menús y flujos del bot se administran en
<span class="font-medium text-gray-500">Chat / Bot Configuración</span>;
WhatsApp usa exactamente los mismos que Telegram y el chat web.
</p>
</div>
<div class="flex justify-end">