From 75e0dca899bb134a270b4993d3737449872b4081 Mon Sep 17 00:00:00 2001 From: Jose Andres Date: Thu, 28 Aug 2025 13:14:00 -0600 Subject: [PATCH] 'New' badge Added --- components/shoe-results-popup.tsx | 64 ++++++++++++++++++- lib/product-api.ts | 102 ++++++++++++++++++++++++++++-- 2 files changed, 158 insertions(+), 8 deletions(-) diff --git a/components/shoe-results-popup.tsx b/components/shoe-results-popup.tsx index 9839a3f..5f4fab9 100644 --- a/components/shoe-results-popup.tsx +++ b/components/shoe-results-popup.tsx @@ -10,7 +10,7 @@ import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { Button } from '@/components/ui/button'; import { ExternalLink, Package, Truck, Shield, Star, ChevronRight, Store, Tag } from 'lucide-react'; -import { fetchProduct, getProductImages, getProductPricing, getProductVariants, getProductCategories, getProductClusters, getStockStatus, getProductGender, getProductSeason, getProductOccasion, type Product } from '@/lib/product-api'; +import { fetchProduct, getProductImages, getProductPricing, getProductVariants, getProductCategories, getProductClusters, getStockStatus, getProductGender, getProductSeason, getProductOccasion, getProductColors, getProductHighlight, type Product } from '@/lib/product-api'; interface ShoeResultsPopupProps { isOpen: boolean; @@ -87,6 +87,16 @@ export default function ShoeResultsPopup({ isOpen, onOpenChange }: ShoeResultsPo + {/* Loading shimmer badge */} +
+
+ + + ... + +
+
+ Cargando producto Obteniendo información del producto
@@ -111,6 +121,8 @@ export default function ShoeResultsPopup({ isOpen, onOpenChange }: ShoeResultsPo const genderInfo = getProductGender(product); const seasonInfo = getProductSeason(product); const occasionInfo = getProductOccasion(product); + const colorsInfo = getProductColors(product); + const highlightInfo = getProductHighlight(product); const selectedVariantData = variants.find(v => v.itemId === selectedVariant); return ( @@ -118,6 +130,25 @@ export default function ShoeResultsPopup({ isOpen, onOpenChange }: ShoeResultsPo + {/* Corner Highlight Badge */} + {highlightInfo && ( +
+
+ + {highlightInfo.icon} + {highlightInfo.label} + +
+
+ )} +
@@ -256,6 +287,37 @@ export default function ShoeResultsPopup({ isOpen, onOpenChange }: ShoeResultsPo )}
+ {/* Colors Section */} + {colorsInfo.length > 0 && ( +
+

+
+ {colorsInfo.length === 1 ? 'Color Disponible' : 'Colores Disponibles'} +

+
+ {colorsInfo.map((colorInfo, index) => ( +
+ {/* Color dot indicator */} +
+ + {colorInfo.label} + +
+ ))} +
+ + {/* Additional color info if available */} + {colorsInfo.length === 1 && ( +
+ Este producto está disponible únicamente en color {colorsInfo[0].label.toLowerCase()} +
+ )} +
+ )} + {/* Size Selection with Stock Info */} {variants.length > 0 && (
diff --git a/lib/product-api.ts b/lib/product-api.ts index 5a2fb2a..a70da1d 100644 --- a/lib/product-api.ts +++ b/lib/product-api.ts @@ -82,9 +82,17 @@ export interface Product { link: string; description: string; items: ProductItem[]; + // Additional fields that appear at product level + Color?: string[]; + Género?: string[]; + Ocasión?: string[]; + Tipo?: string[]; + Acabado?: string[]; + Estilo?: string[]; + Disciplina?: string[]; } -const FIXED_PRODUCT_ID = '169520'; +const FIXED_PRODUCT_ID = '180474'; export async function fetchProduct(productId: string = FIXED_PRODUCT_ID): Promise { try { @@ -184,9 +192,8 @@ export function getStockStatus(availableQuantity: number) { } export function getProductGender(product: Product) { - // Get gender from first available item - const firstItem = product.items[0]; - const gender = firstItem?.Género?.[0]; + // Get gender from product level first, then fallback to items + const gender = product.Género?.[0] || product.items[0]?.Género?.[0]; if (!gender) return null; @@ -226,9 +233,8 @@ export function getProductSeason(product: Product) { } export function getProductOccasion(product: Product) { - // Get occasion from first available item - const firstItem = product.items[0]; - const occasion = firstItem?.Ocasión?.[0]; + // Get occasion from product level first, then fallback to items + const occasion = product.Ocasión?.[0] || product.items[0]?.Ocasión?.[0]; if (!occasion) return null; @@ -240,4 +246,86 @@ export function getProductOccasion(product: Product) { }; return occasionMap[occasion] || { label: occasion, color: 'bg-gray-500/20 text-gray-300 border-gray-500/30', icon: '🏷️' }; +} + +export function getProductColors(product: Product) { + // Get colors from product level first (main approach for this API) + const allColors = new Set(); + + // Check product-level color field (this is where the color is stored in this API) + if (product.Color && Array.isArray(product.Color)) { + product.Color.forEach(color => allColors.add(color)); + } + + // Fallback: check items level + product.items.forEach(item => { + if (item.Color && Array.isArray(item.Color)) { + item.Color.forEach(color => allColors.add(color)); + } + }); + + const colors = Array.from(allColors); + + if (colors.length === 0) return []; + + // Map colors to display info with hex approximations + return colors.map(color => { + const colorLower = color.toLowerCase(); + const colorMap: Record = { + 'negro': { label: 'Negro', hex: '#1f2937', badge: 'bg-gray-800 text-white border-gray-700' }, + 'blanco': { label: 'Blanco', hex: '#f9fafb', badge: 'bg-gray-100 text-gray-900 border-gray-300' }, + 'rojo': { label: 'Rojo', hex: '#dc2626', badge: 'bg-red-500/20 text-red-300 border-red-500/30' }, + 'azul': { label: 'Azul', hex: '#2563eb', badge: 'bg-blue-500/20 text-blue-300 border-blue-500/30' }, + 'verde': { label: 'Verde', hex: '#16a34a', badge: 'bg-green-500/20 text-green-300 border-green-500/30' }, + 'amarillo': { label: 'Amarillo', hex: '#ca8a04', badge: 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30' }, + 'rosa': { label: 'Rosa', hex: '#ec4899', badge: 'bg-pink-500/20 text-pink-300 border-pink-500/30' }, + 'morado': { label: 'Morado', hex: '#9333ea', badge: 'bg-purple-500/20 text-purple-300 border-purple-500/30' }, + 'naranja': { label: 'Naranja', hex: '#ea580c', badge: 'bg-orange-500/20 text-orange-300 border-orange-500/30' }, + 'gris': { label: 'Gris', hex: '#6b7280', badge: 'bg-gray-500/20 text-gray-300 border-gray-500/30' }, + 'marrón': { label: 'Marrón', hex: '#92400e', badge: 'bg-amber-700/20 text-amber-300 border-amber-700/30' }, + 'café': { label: 'Café', hex: '#92400e', badge: 'bg-amber-700/20 text-amber-300 border-amber-700/30' }, + 'beige': { label: 'Beige', hex: '#d6d3d1', badge: 'bg-stone-300/20 text-stone-300 border-stone-300/30' }, + 'dorado': { label: 'Dorado', hex: '#f59e0b', badge: 'bg-yellow-600/20 text-yellow-300 border-yellow-600/30' }, + 'plateado': { label: 'Plateado', hex: '#9ca3af', badge: 'bg-gray-400/20 text-gray-300 border-gray-400/30' } + }; + + // Try exact match first + const exactMatch = colorMap[colorLower]; + if (exactMatch) { + return { ...exactMatch, original: color }; + } + + // Try partial matches for compound colors + const partialMatch = Object.entries(colorMap).find(([key]) => + colorLower.includes(key) || key.includes(colorLower) + ); + + if (partialMatch) { + return { ...partialMatch[1], original: color }; + } + + // Default fallback + return { + label: color, + hex: '#64748b', + badge: 'bg-slate-500/20 text-slate-300 border-slate-500/30', + original: color + }; + }); +} + +export function getProductHighlight(product: Product) { + // Only show badge if clusterHighlights has actual content + if (!product.clusterHighlights || Object.keys(product.clusterHighlights).length === 0) { + return null; + } + + // Show the highlight from clusterHighlights + const highlight = Object.values(product.clusterHighlights)[0]; + return { + label: String(highlight), + style: 'bg-gradient-to-br from-yellow-500 to-orange-600 text-white shadow-lg shadow-yellow-500/50', + icon: '⭐', + type: 'promotion' + }; } \ No newline at end of file