From 409c3de8020aa8f440f664bd2a0737e52f50cea2 Mon Sep 17 00:00:00 2001 From: Jose Andres Date: Thu, 23 Oct 2025 12:23:35 -0600 Subject: [PATCH] changes: add test data and remove chat --- app/page.tsx | 106 ++++++++---- components/shoe-results-popup.tsx | 156 +++++++++++------- lib/product-api.ts | 27 ++- lib/sku-identification.ts | 36 +++- next.config.ts | 12 ++ public/test-product.json | 148 +++++++++++++++++ .../__pycache__/server.cpython-313.pyc | Bin 6581 -> 6588 bytes 7 files changed, 386 insertions(+), 99 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index fdc4eba..cf99a5e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -10,7 +10,7 @@ import { addToHistory, getHistory } from '@/lib/history-storage'; import ShoeResultsPopup from '@/components/shoe-results-popup'; import HistorySidebar from '@/components/history-sidebar'; import { skuIdentificationService } from '@/lib/sku-identification'; -import { fetchProduct, getProductImages, getProductPricing } from '@/lib/product-api'; +import { fetchProduct, getProductImages, getProductPricing, fetchTestProduct } from '@/lib/product-api'; type CameraStatus = 'loading' | 'active' | 'denied' | 'no_devices'; @@ -265,51 +265,99 @@ function HomePageContent() { // Limpiar timeout si la petición completó antes clearTimeout(timeoutId); - console.log('📦 SKU result:', sku); + console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log('📦 RESULTADO FINAL DEL SKU:'); + console.log(' SKU retornado:', sku); + console.log(' Tipo:', typeof sku); + console.log(' Es null:', sku === null); + console.log(' Es string vacío:', sku === ''); + console.log(' Es falsy:', !sku); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); if (sku) { // SKU encontrado - obtener datos reales del producto setDetectedSKU(sku); - // Extraer productId del SKU (primeros 6 caracteres) - // Ejemplo: "18047409" → "180474" - const productId = sku.substring(0, 6); - console.log(`📝 Extrayendo productId: ${productId} del SKU: ${sku}`); - - // Intentar obtener datos reales del producto let shoeWithSKU: Shoe; - try { - const product = await fetchProduct(productId); - if (product) { - const images = getProductImages(product); - const pricing = getProductPricing(product); + // 🧪 FLUJO ESPECIAL: Detectar si es un producto de prueba + if (sku === 'test_product') { + console.log('🧪 ========== FLUJO DE PRUEBA ACTIVADO =========='); + console.log('📦 SKU detectado: test_product'); + console.log('✅ Cargando producto genérico desde JSON...'); + + const testProduct = await fetchTestProduct(); + + if (testProduct) { + const images = getProductImages(testProduct); + const pricing = getProductPricing(testProduct); shoeWithSKU = { id: Date.now().toString(), - name: product.productName, - brand: product.brand, + name: testProduct.productName, + brand: testProduct.brand, price: pricing.isAvailable ? pricing.price.toString() : 'No disponible', image: images[0] || '/placeholder.jpg', sku: sku, timestamp: new Date().toISOString() }; - console.log('✅ Datos del producto obtenidos exitosamente'); + + console.log('🧪 Producto de prueba cargado exitosamente'); } else { - throw new Error('Product not found'); + console.error('❌ Error al cargar producto de prueba, usando fallback'); + shoeWithSKU = { + id: Date.now().toString(), + name: 'Producto de Prueba (Error)', + brand: 'Test', + price: 'No disponible', + image: '/placeholder.jpg', + sku: sku, + timestamp: new Date().toISOString() + }; + } + + console.log('=========================================\n'); + } else { + // FLUJO NORMAL: Extraer productId y obtener datos reales + // Extraer productId del SKU (primeros 6 caracteres) + // Ejemplo: "18047409" → "180474" + const productId = sku.substring(0, 6); + console.log(`📝 Extrayendo productId: ${productId} del SKU: ${sku}`); + + // Intentar obtener datos reales del producto + try { + const product = await fetchProduct(productId); + + if (product) { + const images = getProductImages(product); + const pricing = getProductPricing(product); + + shoeWithSKU = { + id: Date.now().toString(), + name: product.productName, + brand: product.brand, + price: pricing.isAvailable ? pricing.price.toString() : 'No disponible', + image: images[0] || '/placeholder.jpg', + sku: sku, + timestamp: new Date().toISOString() + }; + console.log('✅ Datos del producto obtenidos exitosamente'); + } else { + throw new Error('Product not found'); + } + } catch (error) { + console.warn('⚠️ No se pudieron obtener datos del producto, usando fallback:', error); + // Fallback si falla el fetch + shoeWithSKU = { + id: Date.now().toString(), + name: `Producto ${sku}`, + brand: 'Identificado por IA', + price: 'Precio por consultar', + image: '/placeholder.jpg', + sku: sku, + timestamp: new Date().toISOString() + }; } - } catch (error) { - console.warn('⚠️ No se pudieron obtener datos del producto, usando fallback:', error); - // Fallback si falla el fetch - shoeWithSKU = { - id: Date.now().toString(), - name: `Producto ${sku}`, - brand: 'Identificado por IA', - price: 'Precio por consultar', - image: '/placeholder.jpg', - sku: sku, - timestamp: new Date().toISOString() - }; } const updatedHistory = addToHistory(shoeWithSKU); diff --git a/components/shoe-results-popup.tsx b/components/shoe-results-popup.tsx index ee16797..8fb0721 100644 --- a/components/shoe-results-popup.tsx +++ b/components/shoe-results-popup.tsx @@ -10,7 +10,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from '@/components/ui/button'; import { ExternalLink, Package, Truck, Shield, Star, ChevronRight, Store, Tag, MessageCircle, Sparkles } from 'lucide-react'; -import { fetchProduct, getProductImages, getProductPricing, getProductVariants, getProductCategories, getProductClusters, getStockStatus, getProductGender, getProductSeason, getProductOccasion, getProductColors, getProductHighlight, type Product } from '@/lib/product-api'; +import { fetchProduct, getProductImages, getProductPricing, getProductVariants, getProductCategories, getProductClusters, getStockStatus, getProductGender, getProductSeason, getProductOccasion, getProductColors, getProductHighlight, fetchTestProduct, type Product } from '@/lib/product-api'; import ChatPromptBubble from '@/components/chat-prompt-bubble'; interface ShoeResultsPopupProps { @@ -45,77 +45,107 @@ export default function ShoeResultsPopup({ isOpen, onOpenChange, detectedSKU }: useEffect(() => { if (isOpen && !product) { setLoading(true); - // Extract productId from detectedSKU (first 6 characters) - // Example: "18047409" → "180474" - const productId = detectedSKU?.substring(0, 6); console.log('🔍 ShoeResultsPopup: Buscando producto con SKU:', detectedSKU); - console.log('📦 ProductId extraído:', productId); - fetchProduct(productId).then((data) => { - if (data) { - setProduct(data); - const images = getProductImages(data); - if (images.length > 0) { - // setActiveImageUrl(images[0]); - } - // Set first available variant - const variants = getProductVariants(data); - if (variants.length > 0) { - setSelectedVariant(variants[0].itemId); - if (variants[0].sizes.length > 0) { - setSelectedSize(variants[0].sizes[0]); + // 🧪 FLUJO ESPECIAL: Si es test_product, usar producto genérico + if (detectedSKU === 'test_product') { + console.log('🧪 Producto de prueba detectado en popup'); + + fetchTestProduct().then((testProduct) => { + if (testProduct) { + setProduct(testProduct); + + const images = getProductImages(testProduct); + if (images.length > 0) { + // setActiveImageUrl(images[0]); + } + + // Set first available variant + const variants = getProductVariants(testProduct); + if (variants.length > 0) { + setSelectedVariant(variants[0].itemId); + if (variants[0].sizes.length > 0) { + setSelectedSize(variants[0].sizes[0]); + } } } - } - setLoading(false); - }); + setLoading(false); + }).catch((error) => { + console.error('Error loading test product:', error); + setLoading(false); + }); + } else { + // FLUJO NORMAL: Extract productId from detectedSKU (first 6 characters) + // Example: "18047409" → "180474" + const productId = detectedSKU?.substring(0, 6); + console.log('📦 ProductId extraído:', productId); + + fetchProduct(productId).then((data) => { + if (data) { + setProduct(data); + const images = getProductImages(data); + if (images.length > 0) { + // setActiveImageUrl(images[0]); + } + // Set first available variant + const variants = getProductVariants(data); + if (variants.length > 0) { + setSelectedVariant(variants[0].itemId); + if (variants[0].sizes.length > 0) { + setSelectedSize(variants[0].sizes[0]); + } + } + } + setLoading(false); + }); + } } }, [isOpen, product, detectedSKU]); - // Show chat prompt when popup opens (contextual message) - useEffect(() => { - if (isOpen && product) { - // Wait 2 seconds after popup opens to show prompt - const timer = setTimeout(() => { - const now = Date.now(); - // Only show if we haven't shown a prompt in the last 10 seconds - if (now - lastPromptTime > 10000) { - setPromptMessage('🤖 ¿Dudas sobre este modelo? Nuestra IA está lista para ayudarte'); - setShowChatPrompt(true); - setLastPromptTime(now); - } - }, 2000); + // Show chat prompt when popup opens (contextual message) - DISABLED + // useEffect(() => { + // if (isOpen && product) { + // // Wait 2 seconds after popup opens to show prompt + // const timer = setTimeout(() => { + // const now = Date.now(); + // // Only show if we haven't shown a prompt in the last 10 seconds + // if (now - lastPromptTime > 10000) { + // setPromptMessage('🤖 ¿Dudas sobre este modelo? Nuestra IA está lista para ayudarte'); + // setShowChatPrompt(true); + // setLastPromptTime(now); + // } + // }, 2000); - return () => clearTimeout(timer); - } - }, [isOpen, product, lastPromptTime]); + // return () => clearTimeout(timer); + // } + // }, [isOpen, product, lastPromptTime]); - // Timer de 15 segundos para mostrar prompts periódicos - useEffect(() => { - if (!isOpen) return; + // Timer de 15 segundos para mostrar prompts periódicos - DISABLED + // useEffect(() => { + // if (!isOpen) return; - const interval = setInterval(() => { - const now = Date.now(); - // Check if Chatwoot is not already open and enough time has passed - if (typeof window !== 'undefined' && window.$chatwoot) { - // Only show if chat is not open and enough time since last prompt - if (now - lastPromptTime > 15000) { - const messages = [ - '👋 ¿Necesitas ayuda para encontrar tu talla perfecta?', - '🎯 ¿Buscas algo en específico? Chatea con nuestra IA', - '💬 Pregúntame sobre tallas, disponibilidad o características', - ]; - const randomMessage = messages[Math.floor(Math.random() * messages.length)]; - setPromptMessage(randomMessage); - setShowChatPrompt(true); - setLastPromptTime(now); - } - } - }, 15000); // Every 15 seconds + // const interval = setInterval(() => { + // const now = Date.now(); + // // Check if Chatwoot is not already open and enough time has passed + // if (typeof window !== 'undefined' && window.$chatwoot) { + // // Only show if chat is not open and enough time since last prompt + // if (now - lastPromptTime > 15000) { + // const messages = [ + // '👋 ¿Necesitas ayuda para encontrar tu talla perfecta?', + // '🎯 ¿Buscas algo en específico? Chatea con nuestra IA', + // '💬 Pregúntame sobre tallas, disponibilidad o características', + // ]; + // const randomMessage = messages[Math.floor(Math.random() * messages.length)]; + // setPromptMessage(randomMessage); + // setShowChatPrompt(true); + // setLastPromptTime(now); + // } + // } + // }, 15000); // Every 15 seconds - return () => clearInterval(interval); - }, [isOpen, lastPromptTime]); + // return () => clearInterval(interval); + // }, [isOpen, lastPromptTime]); // Listen to Chatwoot events to manage popup visibility useEffect(() => { @@ -555,13 +585,13 @@ export default function ShoeResultsPopup({ isOpen, onOpenChange, detectedSKU }: `} - {/* Chat Prompt Bubble */} - setShowChatPrompt(false)} onChatClick={handleOpenChat} - /> + /> */} ); diff --git a/lib/product-api.ts b/lib/product-api.ts index ed588bd..15c53ff 100644 --- a/lib/product-api.ts +++ b/lib/product-api.ts @@ -321,7 +321,7 @@ export function getProductHighlight(product: Product) { if (!product.clusterHighlights || Object.keys(product.clusterHighlights).length === 0) { return null; } - + // Show the highlight from clusterHighlights const highlight = Object.values(product.clusterHighlights)[0]; return { @@ -330,4 +330,29 @@ export function getProductHighlight(product: Product) { icon: '⭐', type: 'promotion' }; +} + +/** + * Loads test product data from JSON file + * Used when SKU detection returns "test_product" + * + * The test product data is stored in /public/test-product.json + * and can be edited manually without changing code + */ +export async function fetchTestProduct(): Promise { + try { + const response = await fetch('/test-product.json'); + + if (!response.ok) { + console.error('Failed to load test product JSON:', response.status); + return null; + } + + const testProduct: Product = await response.json(); + console.log('✅ Test product loaded from JSON successfully'); + return testProduct; + } catch (error) { + console.error('Error loading test product:', error); + return null; + } } \ No newline at end of file diff --git a/lib/sku-identification.ts b/lib/sku-identification.ts index 0304502..23f4c51 100644 --- a/lib/sku-identification.ts +++ b/lib/sku-identification.ts @@ -142,15 +142,23 @@ export class SKUIdentificationService { } console.log('✓ Respuesta recibida - Status:', response.status); + console.log('✓ Headers:', Object.fromEntries(response.headers.entries())); // Try to get response as text first const responseText = await response.text(); + console.log('📄 RESPUESTA RAW del servidor Python:'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log(responseText); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); // Try to parse as JSON let data: any; try { data = JSON.parse(responseText); - console.log('✓ Respuesta parseada:', data); + console.log('✓ Respuesta parseada como JSON:'); + console.log(' - status:', data.status, '(tipo:', typeof data.status, ')'); + console.log(' - SKU:', data.SKU, '(tipo:', typeof data.SKU, ')'); + console.log(' - Objeto completo:', JSON.stringify(data, null, 2)); } catch (parseError) { console.error('❌ Error al parsear JSON:', parseError); console.error(' Respuesta recibida:', responseText); @@ -158,25 +166,41 @@ export class SKUIdentificationService { } // Validate response format + console.log('🔍 Validando formato de respuesta...'); if (!this.validateResponse(data)) { + console.error('❌ Validación de formato falló'); throw new Error('Response format validation failed'); } + console.log('✓ Formato de respuesta válido'); // ONLY return SKU if status is EXACTLY true (not false, not null, not undefined) + console.log('🔍 Evaluando condiciones para detectar zapato:'); + console.log(' - ¿data.status === true?', data.status === true); + console.log(' - ¿data.SKU existe?', !!data.SKU); + console.log(' - ¿data.SKU es string?', typeof data.SKU === 'string'); + console.log(' - Valor de data.status:', data.status); + console.log(' - Valor de data.SKU:', data.SKU); + if (data.status === true && data.SKU) { - console.log('✅ ZAPATO ENCONTRADO - SKU:', data.SKU); + console.log('✅✅✅ ZAPATO ENCONTRADO ✅✅✅'); + console.log('📦 SKU DETECTADO:', data.SKU); console.log('=========================================\n'); return data.SKU; } else { + console.log('❌❌❌ ZAPATO NO ENCONTRADO ❌❌❌'); // Log detailed reason for not finding shoe if (data.status === false) { - console.log('❌ ZAPATO NO ENCONTRADO - El servidor reportó status: false'); + console.log('💡 RAZÓN: El servidor reportó status: false'); } else if (data.status === null) { - console.log('❌ ZAPATO NO ENCONTRADO - El servidor reportó status: null'); + console.log('💡 RAZÓN: El servidor reportó status: null'); + } else if (data.status !== true) { + console.log('💡 RAZÓN: status no es exactamente true, es:', data.status, 'tipo:', typeof data.status); } else if (!data.SKU) { - console.log('❌ ZAPATO NO ENCONTRADO - status: true pero SKU es null/vacío'); + console.log('💡 RAZÓN: status es true pero SKU es null/vacío/undefined'); + console.log(' SKU value:', data.SKU); + console.log(' SKU type:', typeof data.SKU); } else { - console.log('❌ ZAPATO NO ENCONTRADO - status:', data.status, 'SKU:', data.SKU); + console.log('💡 RAZÓN: Condición desconocida - status:', data.status, 'SKU:', data.SKU); } console.log('=========================================\n'); return null; diff --git a/next.config.ts b/next.config.ts index 4bdd7a1..9c66251 100644 --- a/next.config.ts +++ b/next.config.ts @@ -17,6 +17,18 @@ const nextConfig: NextConfig = { protocol: 'https', hostname: 'b2cimpulsmx.vteximg.com.br', }, + { + protocol: 'https', + hostname: 'via.placeholder.com', + }, + { + protocol: 'https', + hostname: 'iili.io', + }, + { + protocol: 'https', + hostname: 'm.media-amazon.com', + }, ], }, }; diff --git a/public/test-product.json b/public/test-product.json index e5f9596..20f9c28 100644 --- a/public/test-product.json +++ b/public/test-product.json @@ -173,6 +173,154 @@ "Color": ["Blanco"], "Género": ["Unisex"], "Ocasión": ["Casual"] + }, + { + "itemId": "TEST001-003", + "name": "Zapato de Prueba - Talla 27", + "nameComplete": "Zapato de Prueba - Modelo Demo - Talla 27", + "complementName": "Negro/Blanco", + "ean": "0000000000002", + "referenceId": [{ "Key": "RefId", "Value": "TEST001" }], + "measurementUnit": "un", + "unitMultiplier": 1, + "modalType": null, + "isKit": false, + "images": [ + { + "imageId": "test-img-1", + "imageLabel": "Principal", + "imageTag": "main", + "imageUrl": "https://m.media-amazon.com/images/I/71cNckyVJ0L._AC_SX695_.jpg", + "imageText": "Zapato de Prueba", + "imageLastModified": "2024-01-01T00:00:00.000Z" + }, + { + "imageId": "test-img-2", + "imageLabel": "Principal", + "imageTag": "main", + "imageUrl": "https://m.media-amazon.com/images/I/71uoxNkz6JL._AC_SX695_.jpg", + "imageText": "Zapato de Prueba", + "imageLastModified": "2024-01-01T00:00:00.000Z" + }, + { + "imageId": "test-img-3", + "imageLabel": "Principal", + "imageTag": "main", + "imageUrl": "https://m.media-amazon.com/images/I/71DkrehA2WL._AC_SX695_.jpg", + "imageText": "Zapato de Prueba", + "imageLastModified": "2024-01-01T00:00:00.000Z" + } + ], + "Talla": ["27"], + "variations": [], + "sellers": [ + { + "sellerId": "test-seller", + "sellerName": "Test Seller", + "addToCartLink": "", + "sellerDefault": true, + "commertialOffer": { + "Price": 999.99, + "ListPrice": 1299.99, + "PriceWithoutDiscount": 1299.99, + "FullSellingPrice": 999.99, + "RewardValue": 0, + "PriceValidUntil": "2025-12-31T23:59:59.000Z", + "AvailableQuantity": 3, + "IsAvailable": true, + "GiftSkuIds": [], + "Installments": [ + { + "Value": 999.99, + "InterestRate": 0, + "TotalValuePlusInterestRate": 999.99, + "NumberOfInstallments": 1 + } + ], + "discountHighLight": [], + "teasers": [] + } + } + ], + "Videos": [], + "estimatedDateArrival": null, + "Color": ["Blanco"], + "Género": ["Unisex"], + "Ocasión": ["Casual"] + }, + { + "itemId": "TEST001-004", + "name": "Zapato de Prueba - Talla 28", + "nameComplete": "Zapato de Prueba - Modelo Demo - Talla 28", + "complementName": "Negro/Blanco", + "ean": "0000000000003", + "referenceId": [{ "Key": "RefId", "Value": "TEST001" }], + "measurementUnit": "un", + "unitMultiplier": 1, + "modalType": null, + "isKit": false, + "images": [ + { + "imageId": "test-img-1", + "imageLabel": "Principal", + "imageTag": "main", + "imageUrl": "https://m.media-amazon.com/images/I/71cNckyVJ0L._AC_SX695_.jpg", + "imageText": "Zapato de Prueba", + "imageLastModified": "2024-01-01T00:00:00.000Z" + }, + { + "imageId": "test-img-2", + "imageLabel": "Principal", + "imageTag": "main", + "imageUrl": "https://m.media-amazon.com/images/I/71uoxNkz6JL._AC_SX695_.jpg", + "imageText": "Zapato de Prueba", + "imageLastModified": "2024-01-01T00:00:00.000Z" + }, + { + "imageId": "test-img-3", + "imageLabel": "Principal", + "imageTag": "main", + "imageUrl": "https://m.media-amazon.com/images/I/71DkrehA2WL._AC_SX695_.jpg", + "imageText": "Zapato de Prueba", + "imageLastModified": "2024-01-01T00:00:00.000Z" + } + ], + "Talla": ["28"], + "variations": [], + "sellers": [ + { + "sellerId": "test-seller", + "sellerName": "Test Seller", + "addToCartLink": "", + "sellerDefault": true, + "commertialOffer": { + "Price": 999.99, + "ListPrice": 1299.99, + "PriceWithoutDiscount": 1299.99, + "FullSellingPrice": 999.99, + "RewardValue": 0, + "PriceValidUntil": "2025-12-31T23:59:59.000Z", + "AvailableQuantity": 3, + "IsAvailable": true, + "GiftSkuIds": [], + "Installments": [ + { + "Value": 999.99, + "InterestRate": 0, + "TotalValuePlusInterestRate": 999.99, + "NumberOfInstallments": 1 + } + ], + "discountHighLight": [], + "teasers": [] + } + } + ], + "Videos": [], + "estimatedDateArrival": null, + "Color": ["Blanco"], + "Género": ["Unisex"], + "Ocasión": ["Casual"] } ] } diff --git a/python_server/__pycache__/server.cpython-313.pyc b/python_server/__pycache__/server.cpython-313.pyc index 7e87f123484fc65b51e6744ac3d605becb745e11..a9232a28877eee5b596d032f406d4bc2525dc844 100644 GIT binary patch delta 92 zcmdmLyvLaLGcPX}0}veMew!h@kvEW+o2#HGKczIeBtA1`a}sYiqrS~$Esu*ro+oTB wM8;l^OuiVIe1#|FhMdw0ll3+$ZLVuuU(~kV;d5Er^P;fV1s<=>GW;{S0OTwpU;qFB delta 85 zcmdmEyw#ZZGcPX}0}#ZUJkMa=$Q#JZ!d#qPx;c}#n^E8NvX