Sistema implementado para evitar que los usuarios queden atascados esperando indefinidamente cuando el API del chatbot demora demasiado en responder.
AbortControllerEl componente DynamicLoader ahora incluye mensajes optimizados que se muestran durante la carga:
Los mensajes están diseñados para cubrir hasta 20 segundos (tiempo máximo incluyendo márgenes).
// Configurar timeout de 15 segundos
const controller = new AbortController();
const timeoutId = setTimeout(() => {
console.log("⏰ [CHAT] Timeout alcanzado, cancelando petición...");
controller.abort();
}, 15000); // 15 segundos
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, messages, chatType }),
signal: controller.signal, // 🔑 Señal de cancelación
});
// Limpiar timeout si la petición fue exitosa
clearTimeout(timeoutId);
catch (error) {
clearTimeout(timeoutId);
// Detectar si el error fue por timeout
const isTimeout = error instanceof Error && error.name === 'AbortError';
// Si fue timeout y es el primer intento, reintentar
if (isTimeout && retryCount === 0) {
toast.info("Reintentando...", {
description: "La petición tardó demasiado, reintentando automáticamente.",
});
// Reintentar después de 1 segundo
setTimeout(() => sendMessage(messageToSend, 1), 1000);
return;
}
// Mostrar mensaje de error personalizado
const errorContent = isTimeout
? "La petición tardó demasiado tiempo y fue cancelada automáticamente..."
: "Lo siento, estoy teniendo problemas técnicos...";
}
Usuario envía mensaje
↓
Petición al API inicia (timeout = 15s)
↓
¿Respuesta antes de 15s?
├─ SÍ → Mostrar respuesta ✅
└─ NO → Cancelar petición
↓
¿Es el primer intento?
├─ SÍ → Mostrar toast "Reintentando..."
│ Reintentar automáticamente (1 vez)
│ ↓
│ ¿Respuesta antes de 15s?
│ ├─ SÍ → Mostrar respuesta ✅
│ └─ NO → Mostrar mensaje de error ❌
│
└─ NO → Mostrar mensaje de error ❌
AbortController)// En src/hooks/useChat.ts, línea ~183
const timeoutId = setTimeout(() => {
controller.abort();
}, 15000); // 👈 Cambiar este valor (en milisegundos)
// 15000 = 15 segundos (actual - óptimo para UX)
// 10000 = 10 segundos (más agresivo)
// 20000 = 20 segundos (más tolerante)
// En src/hooks/useChat.ts, línea ~401
if (isTimeout && retryCount === 0) { // 👈 Cambiar la condición
// retryCount === 0: Solo 1 reintento
// retryCount < 2: Hasta 2 reintentos
// etc.
}
Editar el array loadingStates en src/components/chatbot/DynamicLoader.tsx:
const loadingStates: LoadingState[] = [
{
message: "Tu mensaje personalizado...",
icon: <Loader2 className="w-4 h-4 animate-spin" />,
duration: 5 // segundos que dura este mensaje
},
// ... más estados
];
sonner para notificacionesretryCount se pase correctamente