'New' badge Added

This commit is contained in:
2025-08-28 13:14:00 -06:00
parent 2d4ca1ff78
commit 75e0dca899
2 changed files with 158 additions and 8 deletions

View File

@@ -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
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 bg-black/60 backdrop-blur-sm z-[100]" />
<Drawer.Content className="fixed bottom-0 left-0 right-0 mt-24 flex h-[90%] flex-col rounded-t-3xl bg-black/90 backdrop-blur-xl border-t-2 border-white/20 z-[101] shadow-2xl">
{/* Loading shimmer badge */}
<div className="absolute top-4 right-4 z-20">
<div className="bg-gradient-to-r from-blue-400 to-purple-500 px-3 py-1.5 rounded-full text-sm font-bold text-white animate-pulse">
<span className="flex items-center gap-1">
<span></span>
<span>...</span>
</span>
</div>
</div>
<Drawer.Title className="sr-only">Cargando producto</Drawer.Title>
<Drawer.Description className="sr-only">Obteniendo información del producto</Drawer.Description>
<div className="flex-1 flex items-center justify-center">
@@ -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
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 bg-black/60 backdrop-blur-sm z-[100]" />
<Drawer.Content className="fixed bottom-0 left-0 right-0 mt-24 flex h-[90%] flex-col rounded-t-3xl bg-black/90 backdrop-blur-xl border-t-2 border-white/20 z-[101] shadow-2xl">
{/* Corner Highlight Badge */}
{highlightInfo && (
<div className="absolute top-4 right-4 z-20">
<div className={`
${highlightInfo.style}
px-3 py-1.5 rounded-full text-sm font-bold
transform rotate-3 hover:rotate-0
transition-all duration-300 cursor-pointer
animate-pulse hover:animate-none
border border-white/20
`}>
<span className="flex items-center gap-1">
<span>{highlightInfo.icon}</span>
<span>{highlightInfo.label}</span>
</span>
</div>
</div>
)}
<div className="flex-1 overflow-y-auto rounded-t-[24px]">
<div className="sticky top-0 z-10 w-full bg-black/80 backdrop-blur-xl rounded-t-[24px] py-6 border-b border-white/10">
<div className="mx-auto h-1.5 w-16 flex-shrink-0 rounded-full bg-white/40 shadow-lg" />
@@ -256,6 +287,37 @@ export default function ShoeResultsPopup({ isOpen, onOpenChange }: ShoeResultsPo
)}
</div>
{/* Colors Section */}
{colorsInfo.length > 0 && (
<div className="bg-white/5 backdrop-blur-sm rounded-2xl p-6 border border-white/10">
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-3">
<div className="w-2 h-2 bg-blue-400 rounded-full"></div>
{colorsInfo.length === 1 ? 'Color Disponible' : 'Colores Disponibles'}
</h3>
<div className="flex items-center flex-wrap gap-3">
{colorsInfo.map((colorInfo, index) => (
<div key={index} className="flex items-center gap-2">
{/* Color dot indicator */}
<div
className="w-6 h-6 rounded-full border-2 border-white/20 shadow-lg"
style={{ backgroundColor: colorInfo.hex }}
/>
<Badge variant="outline" className={colorInfo.badge}>
{colorInfo.label}
</Badge>
</div>
))}
</div>
{/* Additional color info if available */}
{colorsInfo.length === 1 && (
<div className="mt-4 text-sm text-white/60">
Este producto está disponible únicamente en color {colorsInfo[0].label.toLowerCase()}
</div>
)}
</div>
)}
{/* Size Selection with Stock Info */}
{variants.length > 0 && (
<div className="bg-white/5 backdrop-blur-sm rounded-2xl p-6 border border-white/10">

View File

@@ -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<Product | null> {
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<string>();
// 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<string, { label: string; hex: string; badge: string }> = {
'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'
};
}