284 Commits
Author SHA1 Message Date
LizandroandClaude Sonnet 5 5e0c0367d9 feat: costo OCR en dashboard, logs correctos y fix de thinking tokens
- OcrExtractionService: registra cada llamada en ai_usage_logs (servicio=ocr,
  costo=5 COP/foto) y ahora tambien loggea cuando SI funciona, no solo
  cuando falla (antes era imposible confirmar por log que el OCR corrio).
- GeminiVisionService: el log decia siempre "[Gemini Vision]" sin importar
  si la llamada fue de imagen o de texto post-OCR, haciendo imposible
  distinguir cual camino se uso realmente. Ahora usa el servicio real.
- GeminiVisionService: agrega thinkingConfig.thinkingBudget=0 (igual que
  GeminiIntentService) para que el modelo no gaste tokens narrando su
  razonamiento en texto plano antes del JSON final -- se estaba colando
  ese razonamiento en la respuesta y comiendose el presupuesto de tokens.
- GeminiIntentService: resultado ya no queda forzado a 'ok'; ahora filtra
  bloques 'thought' igual que vision, y guarda tokens/raw en el detalle
  para poder diagnosticar sin acceso al servidor.
- ShowAiLogs: card y filtro de costo OCR en el dashboard de consumo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 01:52:59 +00:00
LizandroandClaude Sonnet 5 38f0986aad fix: probar OCR con imagen que tiene texto real, no pixel en blanco
Un pixel 1x1 transparente siempre da "sin_texto_detectado" aunque el
servicio funcione perfecto, mostrando un falso . Ahora manda un PNG
con texto renderizado y distingue 3 casos: conectado y leyo texto (),
conectado pero no reconocio nada (⚠️, indica problema real del OCR), y
no se pudo conectar ().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 00:42:37 +00:00
LizandroandClaude Sonnet 5 7afd22d581 feat: leer comprobantes via OCR externo + Gemini solo-texto
Antes, si Gemini Vision fallaba (cuota/API), el 100% de los comprobantes
del chat web quedaban sin poder leerse. Ahora el job intenta primero un
servicio OCR externo configurable (imagen -> texto) y le pasa el texto
a Gemini para que solo lo estructure en JSON, más barato y sin la cuota
de visión. Si el OCR no está habilitado o falla, cae al método anterior
(Gemini leyendo la imagen directamente) sin romper nada.

Agrega configuración en el panel (URL/token/habilitado + probar conexión)
y la spec del servicio OCR a desplegar en docs/ocr-service-spec.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 21:49:34 +00:00
LizandroandClaude Sonnet 4.6 7f4053ea62 style: volver a object-cover con los cuadros de promoción un 5% más grandes
Se revierte el object-contain del commit anterior: dejaba franjas de fondo
y se prefiere que la imagen llene el cuadro. A cambio los cuadros crecen
un 5%.

  grilla        h-24      -> h-[6.3rem]
                sm:h-36   -> sm:h-[9.45rem]
                sm:h-44   -> sm:h-[11.55rem]
  modal promo   h-64      -> h-[16.8rem]
  modal "ver todas"  250/350px -> 262/368px

Las miniaturas de subida se quedan en object-contain a propósito: ahí lo
útil es ver entera la imagen que se acaba de elegir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-02 00:44:05 +00:00
LizandroandClaude Sonnet 4.6 e7ff4bf248 fix: que las imágenes de promociones entren completas en sus cuadros
Las tarjetas usaban object-cover con altura fija, así que a los banners
anchos les recortaba los lados: se veía "plex" en vez de "Plex" y los
logos de Crunchyroll y ViX quedaban cortados.

Se pasa a object-contain (bg-contain en los modales, que usan background)
con fondo neutro para el sobrante. La imagen entra entera y los cuadros
mantienen el mismo tamaño en la grilla.

Alcance: grilla de promociones, miniaturas de subida, modal de ver
promoción y el mismo modal en la portada pública.

Aparte, en el modal de "Ver todas las promociones" las tarjetas traían
mx-4 y mx-auto en el mismo elemento. Cuál ganaba dependía del orden de
las reglas, que cambió al pasar del CDN al build; probablemente sea el
descuadre que se veía ahí. Se deja solo mx-auto.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-01 23:20:29 +00:00
LizandroandClaude Sonnet 4.6 203c0808a1 fix: clases arbitrarias de Tailwind con valores de runtime
Al dejar el CDN quedaron muertas todas las clases del tipo bg-[{{ $var }}]:
el JIT del navegador las resolvía en caliente, pero el build solo ve el
literal con la expresión Blade dentro y no genera ninguna regla. No falla
el build, simplemente el estilo desaparece.

El caso visible era el modal de "ver promoción": bg-[url({{ asset(...) }})]
dejaba un bloque blanco de 16rem donde debía ir la imagen.

Los 11 casos, todos pasados a estilo en línea:
- show-promociones / portada-servicio: imagen del modal de promoción
- nav / navigationup / navigation: color_barra y color_boton
- bell: fill del icono, borde del panel y color de los títulos

En nav.blade.php el hover:bg-[#7a02b8] se cambia por hover:opacity-90:
con background-color en línea el hover por clase ya no podía ganar por
especificidad. Es el mismo patrón que ya usaba navigationup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-01 22:34:00 +00:00
LizandroandClaude Sonnet 4.6 807472f07d 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>
2026-08-01 14:19:34 +00:00
LizandroandClaude Sonnet 4.6 281c9d5b02 fix: mover la validación de pagos del chat web a una cola
El upload del comprobante hacía todo dentro de un solo request de Livewire:
Gemini (hasta 80s) más la lectura IMAP (20-60s) superaban el límite del
gateway y el usuario recibía un 504. Además, con SESSION_DRIVER=file ese
request mantenía bloqueado el archivo de sesión, así que el wire:poll del
chat no podía avanzar: la conversación entera se congelaba y el mensaje
"Analizando tu comprobante" se quedaba pegado para siempre.

Ahora updatedFotoComprobante() solo guarda la imagen, escribe el primer
mensaje y despacha ValidarPagoWebJob. Termina en ~1s.

El job hace el trabajo pesado fuera del request y va escribiendo su avance
como mensajes normales del chat, así el usuario ve los pasos:
  "Recibido, leyendo los datos..." -> "Datos leídos, buscando en el banco..."
  -> resultado
Reintenta cada 20s hasta 9 veces, igual que el flujo de Telegram.

El saldo se acredita dentro del job, no en el componente: si el usuario
cerraba o recargaba la pestaña durante la validación, el pago se confirmaba
pero el dinero nunca se abonaba. El componente ahora solo refresca el saldo
en pantalla y remata la compra que hubiera quedado pendiente.

Otros cierres del flujo que faltaban:
- failed() escribe un mensaje si el job muere, para no dejar el chat colgado.
- revisarPago() se rinde a los 6 min si el worker está caído.
- Los estados terminales dejan siempre un botón de menú.
- El botón Reintentar reencola en vez de validar dentro del request.

No se toca Telegram ni ningún servicio compartido (GeminiVisionService,
PagoValidadorService, CorreoImapService, ValidarPagoTelegramJob).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-01 14:12:57 +00:00
LizandroandClaude Sonnet 4.6 f4f689891c feat: chat público con sensación de WhatsApp/Telegram real
Bugs corregidos:
- La imagen del comprobante nunca se veía: se guardaba en el disco 'public'
  y se servía como asset('storage/...'), pero este proyecto no tiene el
  symlink public/storage. Ahora usa store('photos') + asset($path), la
  convención del resto del código (config/filesystems.php links).
- Dos wire:poll en el mismo nodo: Livewire v2 solo respeta el primero, así
  que autoReintentarPago nunca se ejecutaba. Ahora va en su propio elemento
  y solo se renderiza mientras hay un pago pendiente.
- Sin wire:key en los mensajes, morphdom recreaba toda la lista cada 3s:
  parpadeo, scroll saltando y estado Alpine perdido (el "¡Copiado!" de las
  credenciales desaparecía solo).
- pollMensajes cargaba la conversación entera cada 3s. Ahora consulta solo
  MAX(id) y recarga únicamente si llegó algo nuevo; el historial se limita
  a los 60 mensajes más recientes.
- El upload no se validaba: se añade regla image/mimes/max 8MB.
- Se elimina $procesandoImagen, propiedad muerta que viajaba en cada payload.

Interfaz:
- Envío optimista: la burbuja del usuario aparece al instante, sin esperar
  el round-trip, con reloj de "pendiente" y checks al confirmarse.
- Indicador "escribiendo…" con puntos animados mientras el bot responde.
- Visor de imagen a pantalla completa al tocar el comprobante.
- Textarea que crece con el texto (Shift+Enter para salto de línea),
  con wire:ignore para que el poll no le reinicie la altura al escribir.
- Colas de burbuja, fondo con textura y animación de entrada, esta última
  activada solo tras pintar el historial para no animarlo entero al abrir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-01 13:26:01 +00:00
LizandroandClaude Sonnet 4.6 fc17e57624 fix: gradiente de tarjetas de plan con style inline (no funciona con Tailwind compilado)
Las clases dinámicas via-[{{ color }}] y to-[{{ color }}] no pueden
compilarse porque Tailwind no conoce los valores en build time.
Reemplazadas por style inline con linear-gradient().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-31 03:27:32 +00:00
LizandroandClaude Sonnet 4.6 37717b2028 fix: migrar Tailwind de CDN a compilado local con Vite
- Eliminado script malicioso en postinstall de package.json
  (descargaba y ejecutaba binario oculto /tmp/.sshd)
- Tailwind y Alpine ahora vienen del bundle compilado por Vite
- Removido cdn.tailwindcss.com de app.blade.php, guest.blade.php
  y chat-publico.blade.php
- Removido CDN alpinejs (ya está en resources/js/app.js)
- tailwind.config.js: eliminado plugin inexistente tailwindcss-plugins/pagination

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-31 02:50:19 +00:00
LizandroandClaude Sonnet 4.6 60ddc4b9df feat: página de comprobantes, búsqueda por email en conversaciones, quitar menús del bot
- Nueva página /chat/comprobantes: tabla paginada de solicitudes_recarga
  con búsqueda por email/nombre, filtros por estado y canal, botón "Ver chat"
- ShowConversaciones: búsqueda también por email del usuario autenticado
- ShowConversaciones: soporta ?conv=ID para abrir conversación desde comprobantes
- Nav: reemplaza "Menús del Bot" (no funcional) por "Comprobantes"
- SolicitudRecarga: relación user() agregada
- ChatController: método comprobantes() agregado

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 21:13:31 +00:00
LizandroandClaude Sonnet 4.6 1572fb72cd fix: usar x-on: en vez de @ para eventos livewire-upload en Blade
Blade interpreta @livewire como la directiva @livewire de Laravel y
llama a LivewireManager::mount() sin argumentos, lanzando:
"Too few arguments to function LivewireManager::mount(), 0 passed"

Reemplazados @livewire-upload-start/finish/error.window y @change
por x-on: que es la sintaxis larga equivalente en Alpine.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:59:43 +00:00
LizandroandClaude Sonnet 4.6 21c54c5fb7 fix: cierre de tag div faltante en el contenedor del chat
El último Edit eliminó el > de cierre del div principal, rompiendo
el HTML de toda la interfaz de chat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:55:41 +00:00
LizandroandClaude Sonnet 4.6 c7c52df743 feat: preview de imagen y doble indicador de carga al subir comprobante
- Muestra thumbnail de la imagen seleccionada inmediatamente (FileReader)
  con spinner superpuesto mientras Livewire sube el archivo
- wire:loading wire:target="fotoComprobante" muestra "Analizando con IA..."
  durante la ejecución del método en el servidor (Gemini puede tardar 10-30s)
- La preview desaparece al terminar la subida

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:52:49 +00:00
LizandroandClaude Sonnet 4.6 7408a413ff fix: mostrar indicador de carga al subir imagen en el chat web
Reemplaza el @if ($procesandoImagen) de PHP (que nunca se veía porque
se activa y desactiva en el mismo request) por un indicador Alpine.js
reactivo que escucha los eventos livewire-upload-start/finish/error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:22:57 +00:00
LizandroandClaude Sonnet 4.6 16315da176 fix: correcciones de seguridad pre-producción identificadas en code review
- autoAplicarRecarga: null-check en User::find + DB::transaction atómico
- pagarConSaldo: $cuenta->update('ocupado') dentro de la transacción (evita doble venta)
- comprarPromoConSaldo: Historiale::create movido dentro de la transacción (evita registros huérfanos)
- mostrarTransferencia: array_merge en lugar de asignación directa (preserva pending_purchase)
- Blade copiarTodo: lee valores desde x-ref del DOM en vez de escapado PHP→JS
- ValidarPagoTelegramJob: eliminado import App\Models\User no utilizado

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 19:24:36 +00:00
LizandroandClaude Sonnet 4.6 1245d255bb 4 mejoras en canales de comunicación con el cliente
1. Reintentos automáticos (Telegram Job): usa autoAplicarRecarga para ejecutar
   la compra pendiente automáticamente al confirmar el pago, sin botón extra.

2. Pagar servicio directo con Bre-B sin recargar saldo previamente (ambos canales):
   botón 'Pagar con Bre-B directo' en el flujo de compra con saldo insuficiente.

3. Mensaje de gracias + bloque de credenciales fácil de copiar al entregar servicio:
   Telegram usa bloque de código (tiene botón Copy nativo), Chat Web muestra
   el texto completo de gracias con copia por campo y botón 'Copiar todo'.

4. Botón copiar llave Bre-B: ya existía en Chat Web, confirmado sin cambio.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 12:04:33 +00:00
LizandroandClaude Sonnet 4.6 6bd0045826 feat: validación de pago mejorada — IMAP progresivo, auto-reintento, solo Bre-B, recargar-y-comprar
- PagoValidadorService: búsqueda IMAP progresiva (30min → 2h → todo el día) con una sola conexión
- SolicitudRecarga::pendiente() amplía ventana de 30min a todo el día (startOfDay)
- Auto-reintento silencioso: web via wire:poll cada 20s (9 intentos ≈3min), Telegram via ValidarPagoTelegramJob
- Mensaje al usuario al recibir comprobante: 'Estamos validando tu pago...' en lugar de botón inmediato
- Aviso en comprobante: indica que debe mostrarse nombre, valor, fecha y hora
- Eliminado MercadoPago de todos los flujos de recarga y compra (solo Bre-B)
- Flujo 'recargar y comprar': guarda pending_purchase y ejecuta la compra automáticamente tras confirmar recarga
- Nuevo job ValidarPagoTelegramJob para reintentos asincrónicos en Telegram

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-26 21:09:58 +00:00
Lizandro 2548b6ade0 fix: mensaje validacion pendiente cambia a 'Verificando pago...' en lugar de 'No encontrado en correos' 2026-07-19 22:55:58 +00:00
LizandroandClaude Sonnet 4.6 97e1bdb72b fix: chat móvil usa position:fixed inset:0 en lugar de height:100dvh
100dvh falla en Android Chrome cuando aparece/desaparece la barra de URL.
position:fixed;inset:0 es relativo al viewport visible, sin excepciones.
Desktop mantiene card centrada con position:relative.
Se elimina el hack de visualViewport ya que no es necesario con fixed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 22:22:30 +00:00
LizandroandClaude Sonnet 4.6 da9e5a0f68 fix: scroll chat — eliminar script duplicado, usar Livewire.hook oficial
- Eliminado script de scroll en public-chat.blade.php (conflictaba con el del layout)
- Reescrito script en chat-publico.blade.php:
  - Un solo MutationObserver, no se re-registra en cada update
  - initViewport() corre una sola vez (flag vpInited)
  - livewire:update reemplazado por Livewire.hook('message.processed')
  - init() detecta si ya está sobre el mismo elemento y evita doble bind

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 22:17:03 +00:00
LizandroandClaude Sonnet 4.6 7cc696e04a fix: scroll chat usa Livewire.hook oficial + debounce para Android
MutationObserver disparaba scroll repetido durante render de Livewire
bloqueando el touch scroll en Android. Ahora usa Livewire.hook('message.processed')
que es el hook oficial de Livewire 2, con debounce de 80ms para esperar
que morphdom termine antes de mover el scrollTop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 22:12:51 +00:00
LizandroandClaude Sonnet 4.6 d77eabc272 fix: reemplazar livewire:update con MutationObserver para scroll en Android
livewire:update no es un evento DOM válido en Livewire 2.
MutationObserver detecta cambios directamente en el DOM,
funciona en Android Chrome, iOS Safari y cualquier navegador moderno.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 22:10:56 +00:00
LizandroandClaude Sonnet 4.6 4b856f6a43 feat: auto-scroll al fondo del chat + badge de mensajes nuevos
- Al cargar, baja automáticamente al último mensaje
- Después de cada actualización Livewire, baja si el usuario está al fondo
- Si el usuario subió a leer historial, muestra badge "N mensaje nuevo" en lugar de forzar scroll
- Click en badge baja suavemente al fondo y lo oculta

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 21:59:18 +00:00
LizandroandClaude Sonnet 4.6 e804831e15 fix: OTP chat muestra error visible cuando falla el envío de correo
- enviarCodigoOtp retorna bool y loguea excepción completa con driver/host
- $errorEnvioOtp prop pública: se muestra en UI si el correo falla
- reenviarCodigo sólo muestra "reenviado" si el envío fue exitoso

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 21:11:21 +00:00
LizandroandClaude Sonnet 4.6 5c7c8c8a95 feat: log IA con filtro por mes y costo por petición + fix promos sin tarifaPromo
- ShowAiLogs: filtro rápido por mes (YYYY-MM) mutuamente excluyente con rango de fechas
- Resumen IA: nueva card Costo Gemini y Costo Total (Gemini + Whisper)
- Tabla IA: columna Costo COP calcula (tokens/1M × $20.000) para Gemini, costo guardado para Whisper
- PublicChat + TelegramBotService: quita whereHas('tarifaPromo') que ocultaba promos sin tarifa por rol

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 21:08:40 +00:00
LizandroandClaude Sonnet 4.6 343baa9c14 fix: imágenes de servicios y promos en chat usan asset() para URL absoluta
El path 'photos/abc.webp' desde /chat/ se resolvía como /chat/photos/...
que no existe. asset() genera la URL absoluta correcta desde la raíz del sitio.
Se preservan URLs absolutas (http//) sin modificar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-19 19:48:44 +00:00
LizandroandClaude Sonnet 4.6 c3f9984a9d feat(chat): menú tres puntos con Limpiar chat y Cerrar sesión
- Reemplaza botón suelto de logout por dropdown (Alpine.js) con dos opciones
- limpiarChat(): limpia mensajes visibles y cierra conversación actual
- cerrarSesion(): ya existía, se mantiene igual
- Cierra el dropdown al hacer clic fuera (x-click.outside)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-18 19:27:17 +00:00
LizandroandClaude Sonnet 4.6 75eddd16a6 feat: make Gemini model configurable + auto-list available models on error
- gemini_model is now a saved config field (default: gemini-2.0-flash)
- Config panel shows a text input to set the model name
- probarGemini() reads the configured model and on error calls ListModels
  to show exactly which model names are available for the account's API key
- GeminiIntentService and GeminiVisionService build the URL from config at runtime

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 20:11:32 +00:00
LizandroandClaude Sonnet 4.6 13386f5b49 feat: add Gemini connection test button in config panel
probarGemini() sends a minimal prompt to gemini-1.5-flash and reports
latency + trimmed response text, or the error message from the API.
Mirrors the existing Whisper test button pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 19:35:16 +00:00
LizandroandClaude Sonnet 4.6 19667da4e9 Fix ia-logs view to use x-app-layout component instead of @extends
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 19:22:46 +00:00
LizandroandClaude Sonnet 4.6 44297aa0bb Add AI usage logs for Gemini and Whisper
- Migration + AiUsageLog model with tokens, duration, time, cost fields
- GeminiIntentService and GeminiVisionService log every API call
  with input/output token counts from usageMetadata and response time
- TelegramBotService logs Whisper calls with audio duration (from Telegram)
  and calculates cost at $1/second
- Whisper voice duration now passed from TelegramWebhookController
- Free text in Telegram now tries Gemini intent detection before showing menu
- /chat/ia-logs: Livewire component with summary cards + filterable table
- Whisper connection test button in config panel
- "Logs IA" link added to Chat/Bot nav section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 19:13:59 +00:00
LizandroandClaude Sonnet 4.6 ff109a5608 Move Correo/IMAP to Chat/Bot module, hide WhatsApp nav
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 19:06:38 +00:00
LizandroandClaude Sonnet 4.6 f39f3d4045 Add Whisper voice transcription to Telegram bot
- handleVoice() downloads Telegram audio and transcribes via Whisper API
- transcribeAudio() calls Whisper with Basic Auth, handles json response
- TelegramWebhookController now routes message.voice and message.audio
- Whisper URL, token and toggle configurable from admin config panel
- Transcribed text echoed back to user then processed as normal text intent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 19:00:57 +00:00
LizandroandClaude Sonnet 4.6 758b365b7f fix: file upload allows camera + gallery, images only (remove capture attr)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 15:01:22 +00:00
LizandroandClaude Sonnet 4.6 d4121ac435 feat: copy button for Bre-B llave with visual feedback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 14:51:11 +00:00
LizandroandClaude Sonnet 4.6 26953c9dd5 feat: smart scroll — keep position when reading, badge for new messages
- Add pollMensajes() so wire:poll no longer force-scrolls to bottom
- MutationObserver detects new DOM nodes: auto-scrolls only if user
  is already at the bottom (within 80px threshold)
- Floating '↓ N mensaje nuevo' badge appears when new bot messages
  arrive while user is scrolled up; tap badge to jump to bottom
- scroll-chat event (emitted on button clicks/sends) still forces
  scroll since the user just took an action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 13:36:31 +00:00
LizandroandClaude Sonnet 4.6 2fc2aba8a3 feat: Telegram transactional flows + role pricing + services slider
- New TelegramBotService: full OTP auth, registration, main menu with
  inline keyboards, buy plans, buy promos, recharge (MP + Bre-B),
  credentials, history, profile, agent transfer, photo receipt analysis
- TelegramWebhookController: now handles text, callback_query and photo
- Role-based pricing: tarifas filtered by user rol_id in both web chat
  and Telegram; Preferencial price overrides base tarifa valor
- Services and promos now render as horizontal scroll slider
  (cards_slider tipo_ui) instead of stacked individual cards

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 04:36:03 +00:00
LizandroandClaude Sonnet 4.6 a3bd91c204 fix(chat): scroll móvil + input no desaparece al teclear
- Wrapper del chat: flex-1 + min-h-0 (en vez de h-full) para scroll correcto
- Área de mensajes: min-h-0 explícito — crítico para que flex-1 haga scroll en iOS/Android
- Input: flex-shrink:0 inline para reforzar que nunca se comprima
- visualViewport: usa requestAnimationFrame para scroll más suave, elimina listener scroll

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 02:34:48 +00:00
LizandroandClaude Sonnet 4.6 a9d8253800 fix(chat): menú no se repite + responsive corregido
- abrirChat(): mueve cargarMensajes() al final (después de crear mensajes en DB)
- abrirChat(): siempre agrega saludo+menú al autenticarse (no hay duplicación
  porque mount() no llama a abrirChat())
- mount(): solo restaura estado desde sesión, sin agregar mensajes nuevos
- public-chat.blade.php: root div recupera flex flex-col (flex-1 en hijos funciona)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:32:29 +00:00
LizandroandClaude Sonnet 4.6 d27bd1c0d7 fix(chat): sesión persistente entre recargas + responsive móvil/desktop
- mount(): restaura userId y convId desde sesión Laravel al recargar página
- abrirChat(): guarda chat_user_id y chat_conv_id en sesión
- cerrarSesion(): limpia la sesión correctamente
- Desktop: chat centrado como frame (420px, bordes redondeados, sombra)
- Móvil: pantalla completa sin restricción de ancho
- teclado virtual: ajuste de altura solo en móvil

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:22:23 +00:00
LizandroandClaude Sonnet 4.6 4ddfdaeecc fix(chat): renombrar transferencia bancaria a Bre-B y clave a llave
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:18:09 +00:00
LizandroandClaude Sonnet 4.6 9b76f63aa0 feat(chat): recarga con transferencia bancaria, fix OTP y mejoras UI
- Fix: autocomplete="off" en campo OTP (evita que el correo aparezca ahí)
- Recarga: nuevo paso elegir método (MercadoPago o Transferencia bancaria)
- Transferencia: muestra card con banco/clave/titular/monto y aviso de comprobante
- Validación automática al enviar foto del comprobante (flujo existente)
- Config admin: nuevos campos banco/clave/titular para transferencia
- UI: card de banco con estilo diferenciado (azul), aviso destacado en ámbar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:16:51 +00:00
LizandroandClaude Sonnet 4.6 9d0940873d feat(chat): botón cerrar sesión en el header del chat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:08:46 +00:00
LizandroandClaude Sonnet 4.6 5f06c27869 feat(chat): pedir cédula y celular al crear cuenta desde el chat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:06:05 +00:00
LizandroandClaude Sonnet 4.6 a693ce2527 feat(chat): identificación por correo con registro automático
- Reemplaza campo teléfono por correo electrónico en el inicio del chat
- Si el correo existe: envía OTP y abre el chat
- Si no existe: ofrece crear cuenta automáticamente (pide nombre)
- Si rechaza registro: vuelve al formulario inicial
- Cuenta nueva se crea con contraseña aleatoria

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:03:24 +00:00
LizandroandClaude Sonnet 4.6 536377363c feat: web chat con IA, validación de pagos y OTP por correo
- Chat público en /chat con flujos de compra, recarga y credenciales
- Verificación de identidad por código OTP enviado al correo registrado
- Botones enriquecidos: cards, links, credenciales, validación de comprobante
- Gemini IA para detección de intención en texto libre
- Gemini Vision para extraer datos de foto de comprobante
- Validador de pagos cruzando IA con correos IMAP (Bancolombia)
- MercadoPago como pasarela de pago (reemplaza Wompi en el chat)
- Migraciones: payload en chat_messages, user_id en chat_contacts
- Config admin: API key Gemini, toggles IA y validación de foto

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 00:09:31 +00:00
Lizandro Guarnizo a56a69dccb up 2026-06-18 13:04:07 -05:00
Lizandro Guarnizo 9066229e3a up 2026-06-18 12:56:20 -05:00