fix: indicador de subida colgado y apertura directa de servicio por nombre

1) "Subiendo comprobante..." no se quitaba nunca.

   La miniatura solo se limpiaba con livewire-upload-finish, que cubre la
   subida del archivo pero no el procesado posterior. Y updatedFotoComprobante
   tenía tres rutas de salida tempranas (sin convId, análisis deshabilitado,
   validación fallida) que ni siquiera llegaban al finally.

   Ahora todo el cuerpo va dentro de un try/finally que emite siempre
   'comprobante-listo', y el cliente limpia con ese evento del servidor en vez
   de depender de los eventos internos de Livewire. Se añade además un timeout
   de 45s como red de seguridad.

2) "cuenta de hbo" abría el listado completo en vez de HBO.

   El regex de servicios.listar tenía las marcas quemadas (netflix|hbo|disney…),
   así que capturaba la frase y devolvía data vacía antes de que la IA pudiera
   extraer el nombre. Telegram no falla porque sus keywords son solo intents
   genéricos y deja los específicos al agente.

   - Se sacan las marcas del regex genérico.
   - Nuevo detectarServicioPorNombre(): resuelve contra los nombres reales de
     la tabla servicios (nada quemado en código, sin llamada a la IA) y abre
     el servicio pedido.
   - clickBoton('servicios.listar') ahora honra data.servicio, como hace
     dispatchAction en Telegram.
   - El prompt de GeminiIntentService pide data.servicio. Este servicio lo usa
     solo el chat web; Telegram usa GeminiAgentService y no se toca.

   El patrón de credenciales se refuerza y pasa a evaluarse primero, para que
   "no puedo entrar a netflix" siga siendo soporte y no una intención de compra.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro
2026-08-01 14:19:34 +00:00
co-authored by Claude Sonnet 4.6
parent 281c9d5b02
commit 807472f07d
3 changed files with 128 additions and 37 deletions
@@ -191,8 +191,12 @@
}
}"
x-on:livewire-upload-start.window="uploading = true"
x-on:livewire-upload-finish.window="uploading = false; previewUrl = null"
x-on:livewire-upload-error.window="uploading = false; previewUrl = null">
x-on:livewire-upload-finish.window="uploading = false"
x-on:livewire-upload-error.window="uploading = false; previewUrl = null"
{{-- Quien manda es el servidor: emite 'comprobante-listo' en todas
sus rutas de salida. Los eventos de upload de Livewire solo
cubren la subida del archivo, no el procesado posterior. --}}
x-on:comprobante-listo.window="uploading = false; previewUrl = null">
{{-- Header --}}
<div class="bg-[#202c33] shadow-md flex-shrink-0 safe-top safe-left safe-right">
@@ -743,7 +747,16 @@
{{-- Botón adjuntar foto --}}
<label class="flex-shrink-0 cursor-pointer text-[#8696a0] active:text-[#00a884] transition" style="min-width:44px; min-height:44px; display:flex; align-items:center; justify-content:center;">
<input type="file" wire:model="fotoComprobante" accept="image/*" class="hidden"
x-on:change="if($event.target.files[0]){ let r=new FileReader(); r.onload=e=>previewUrl=e.target.result; r.readAsDataURL($event.target.files[0]); }"
x-on:change="
if ($event.target.files[0]) {
let r = new FileReader();
r.onload = e => previewUrl = e.target.result;
r.readAsDataURL($event.target.files[0]);
// Red de seguridad: si la respuesta nunca llega,
// no dejamos la miniatura girando para siempre.
setTimeout(() => { previewUrl = null; uploading = false; }, 45000);
}
"
>
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" />