changes: Request cancelation and handle reset
This commit is contained in:
78
app/page.tsx
78
app/page.tsx
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useRef, useState, useCallback, Suspense } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { Camera, History, VideoOff, Settings, Video } from 'lucide-react';
|
||||
import { Camera, History, VideoOff, Settings, Video, X } from 'lucide-react';
|
||||
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { type Shoe } from '@/lib/shoe-database';
|
||||
@@ -31,6 +31,7 @@ function HomePageContent() {
|
||||
const [isSettingsPanelOpen, setSettingsPanelOpen] = useState(false);
|
||||
const [isScanning, setIsScanning] = useState(false);
|
||||
const [notFoundMessage, setNotFoundMessage] = useState(false);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
// Effect to clean up the stream when component unmounts or stream changes
|
||||
useEffect(() => {
|
||||
@@ -164,6 +165,22 @@ function HomePageContent() {
|
||||
const handleScan = async () => {
|
||||
if (!videoRef.current || isScanning) return;
|
||||
|
||||
// 1. Cancelar petición anterior si existe
|
||||
if (abortControllerRef.current) {
|
||||
console.log('🚫 Cancelando petición anterior...');
|
||||
abortControllerRef.current.abort();
|
||||
}
|
||||
|
||||
// 2. Crear nuevo AbortController para esta petición
|
||||
const controller = new AbortController();
|
||||
abortControllerRef.current = controller;
|
||||
|
||||
// 3. Configurar timeout de 30 segundos
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.log('⏱️ Timeout alcanzado - cancelando petición...');
|
||||
controller.abort();
|
||||
}, 30000);
|
||||
|
||||
try {
|
||||
setIsScanning(true);
|
||||
setNotFoundMessage(false); // Limpiar mensaje anterior
|
||||
@@ -181,8 +198,12 @@ function HomePageContent() {
|
||||
|
||||
console.log('🔍 Llamando al servidor Python para identificar SKU...');
|
||||
|
||||
// Call SKU identification service
|
||||
const sku = await skuIdentificationService.identifySKU(imageData);
|
||||
// Call SKU identification service with abort signal
|
||||
const sku = await skuIdentificationService.identifySKU(imageData, controller.signal);
|
||||
|
||||
// Limpiar timeout si la petición completó antes
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
console.log('📦 SKU result:', sku);
|
||||
|
||||
if (sku) {
|
||||
@@ -217,6 +238,15 @@ function HomePageContent() {
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// Limpiar timeout en caso de error
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// No mostrar error si fue cancelación
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.log('🚫 Escaneo cancelado');
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('❌ Error en identificación:', error);
|
||||
setDetectedSKU(null);
|
||||
setNotFoundMessage(true);
|
||||
@@ -227,6 +257,16 @@ function HomePageContent() {
|
||||
}, 3000);
|
||||
} finally {
|
||||
setIsScanning(false);
|
||||
abortControllerRef.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelScan = () => {
|
||||
if (abortControllerRef.current) {
|
||||
console.log('🚫 Usuario canceló el escaneo');
|
||||
abortControllerRef.current.abort();
|
||||
setIsScanning(false);
|
||||
abortControllerRef.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -507,22 +547,46 @@ function HomePageContent() {
|
||||
{/* Main Capture Button - Larger */}
|
||||
<button
|
||||
onClick={handleScan}
|
||||
disabled={isScanning}
|
||||
className='group relative'
|
||||
>
|
||||
<div className="w-16 h-16 bg-gradient-to-br from-red-500/40 to-pink-500/40 rounded-full flex items-center justify-center border-2 border-white/40 hover:from-red-500/60 hover:to-pink-500/60 transition-all duration-300 transform hover:scale-110 shadow-2xl">
|
||||
<div className={`w-16 h-16 bg-gradient-to-br rounded-full flex items-center justify-center border-2 border-white/40 transition-all duration-300 shadow-2xl ${
|
||||
isScanning
|
||||
? 'from-blue-500/40 to-purple-500/40 animate-pulse cursor-not-allowed'
|
||||
: 'from-red-500/40 to-pink-500/40 hover:from-red-500/60 hover:to-pink-500/60 transform hover:scale-110'
|
||||
}`}>
|
||||
<div className="w-12 h-12 bg-white/20 rounded-full flex items-center justify-center">
|
||||
<Camera size={28} className="text-white drop-shadow-lg" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute -top-12 left-1/2 transform -translate-x-1/2 bg-black/80 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap">
|
||||
Detectar Zapato
|
||||
{isScanning ? 'Analizando...' : 'Detectar Zapato'}
|
||||
</div>
|
||||
{/* Pulsing Ring */}
|
||||
<div className="absolute inset-0 rounded-full border-2 border-red-400/50 animate-ping"></div>
|
||||
{/* Pulsing Ring - solo cuando NO está escaneando */}
|
||||
{!isScanning && (
|
||||
<div className="absolute inset-0 rounded-full border-2 border-red-400/50 animate-ping"></div>
|
||||
)}
|
||||
{/* Glow Effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-red-500/30 to-pink-500/30 rounded-full blur opacity-0 group-hover:opacity-100 transition-opacity duration-300 transform scale-150"></div>
|
||||
</button>
|
||||
|
||||
{/* Cancel Button - solo visible cuando está escaneando */}
|
||||
{isScanning && (
|
||||
<button
|
||||
onClick={handleCancelScan}
|
||||
className='group relative animate-in fade-in slide-in-from-bottom-2 duration-300'
|
||||
>
|
||||
<div className="w-12 h-12 bg-gradient-to-br from-orange-500/40 to-red-500/40 rounded-full flex items-center justify-center border border-white/40 hover:from-orange-500/60 hover:to-red-500/60 transition-all duration-300 transform hover:scale-110">
|
||||
<X size={20} className="text-white drop-shadow-sm" />
|
||||
</div>
|
||||
<div className="absolute -top-10 left-1/2 transform -translate-x-1/2 bg-black/80 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap">
|
||||
Cancelar
|
||||
</div>
|
||||
{/* Glow Effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-500/20 to-red-500/20 rounded-full blur opacity-0 group-hover:opacity-100 transition-opacity duration-300 transform scale-150"></div>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* History Button */}
|
||||
<button
|
||||
onClick={() => setHistoryOpen(true)}
|
||||
|
||||
@@ -64,9 +64,10 @@ export class SKUIdentificationService {
|
||||
/**
|
||||
* Identify product SKU from shoe image
|
||||
* @param imageData - Image data captured from video
|
||||
* @param signal - Optional AbortSignal for cancellation
|
||||
* @returns Promise<string | null> - SKU if found, null otherwise
|
||||
*/
|
||||
async identifySKU(imageData: ImageData): Promise<string | null> {
|
||||
async identifySKU(imageData: ImageData, signal?: AbortSignal): Promise<string | null> {
|
||||
try {
|
||||
console.log('\n🔍 ========== IDENTIFICACIÓN DE SKU ==========');
|
||||
console.log('📊 Dimensiones imagen:', imageData.width, 'x', imageData.height);
|
||||
@@ -101,6 +102,7 @@ export class SKUIdentificationService {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
mode: 'cors', // Need CORS headers from server
|
||||
signal, // Pass AbortSignal for cancellation support
|
||||
headers: {
|
||||
// Don't set Content-Type, let browser set it with boundary for multipart/form-data
|
||||
'ngrok-skip-browser-warning': 'true', // Skip ngrok browser warning
|
||||
@@ -154,6 +156,13 @@ export class SKUIdentificationService {
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// Handle AbortError separately (request was cancelled)
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.log('🚫 PETICIÓN CANCELADA por el usuario o timeout');
|
||||
console.log('=========================================\n');
|
||||
return null;
|
||||
}
|
||||
|
||||
console.error('❌ ERROR EN IDENTIFICACIÓN:', error);
|
||||
console.error(' Detalles:', {
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
|
||||
Reference in New Issue
Block a user