117 lines
4.2 KiB
TypeScript
117 lines
4.2 KiB
TypeScript
import { Product, ProductColorVariant } from '@/types';
|
|
import { generateAttractiveProductCopy } from './attractive-copy';
|
|
import { getProductSocialProof } from './social-proof';
|
|
|
|
/**
|
|
* Normaliza el nombre del modelo para identificar automáticamente productos del mismo modelo
|
|
* pero con diferente colorway (ej: "Adidas Samba", "Adidas Samba Blanco y Negro", "Adidas Samba Gum")
|
|
*/
|
|
export function normalizeModelKey(name: string, brand?: string): string {
|
|
let clean = (name || '').toLowerCase();
|
|
|
|
if (brand) {
|
|
const b = brand.toLowerCase();
|
|
if (!clean.includes(b)) clean = `${b} ${clean}`;
|
|
}
|
|
|
|
clean = clean
|
|
.replace(
|
|
/\b(blanco|negro|gris|azul|verde|rojo|rosa|beige|gum|cream|shadow|panda|retro|og|h|dama|caballero|unisex|ref|\d{3}-\d{3}|edition|edicion|clasico|clasica|sneakers|zapatillas|calzado|para|de|y|en|con)\b/gi,
|
|
''
|
|
)
|
|
.replace(/[^a-z0-9\s]/g, '')
|
|
.replace(/\s+/g, '-')
|
|
.replace(/^-+|-+$/g, '');
|
|
|
|
return clean || 'sneakers';
|
|
}
|
|
|
|
/**
|
|
* Agrupa una lista de productos que corresponden al mismo modelo pero diferente color
|
|
* Convirtiéndolos en un único producto con múltiples colorways (ProductColorVariant).
|
|
*/
|
|
export function groupProductsByModel(products: Product[]): Product[] {
|
|
const modelMap = new Map<string, Product>();
|
|
|
|
for (const prod of products) {
|
|
const brand = prod.brand || 'Sneakers';
|
|
const baseModelName = prod.modelName || prod.name;
|
|
const modelKey = normalizeModelKey(baseModelName, brand);
|
|
|
|
if (!modelMap.has(modelKey)) {
|
|
// Inicializar el primer producto para este modelo
|
|
const colors: ProductColorVariant[] =
|
|
Array.isArray(prod.colors) && prod.colors.length > 0
|
|
? [...prod.colors]
|
|
: [
|
|
{
|
|
id: `color-${Date.now()}-1`,
|
|
colorName: prod.colorName || 'Estilo Principal',
|
|
image: prod.images[0],
|
|
images: prod.images,
|
|
sizes: prod.sizes || ['37', '38', '39', '40', '41', '42', '43'],
|
|
inStock: prod.inStock !== false,
|
|
},
|
|
];
|
|
|
|
const proof = getProductSocialProof(prod);
|
|
|
|
modelMap.set(modelKey, {
|
|
...prod,
|
|
modelName: baseModelName,
|
|
colors,
|
|
description: prod.description || generateAttractiveProductCopy(prod),
|
|
rating: prod.rating || proof.rating,
|
|
reviewsCount: prod.reviewsCount || proof.reviewsCount,
|
|
salesCount: prod.salesCount || proof.salesCount,
|
|
});
|
|
} else {
|
|
// El modelo ya existe: fusionar colorways, fotos y tallas
|
|
const existing = modelMap.get(modelKey)!;
|
|
const existingColors = existing.colors || [];
|
|
|
|
// Extraer los colorways del nuevo producto
|
|
const incomingColors: ProductColorVariant[] =
|
|
Array.isArray(prod.colors) && prod.colors.length > 0
|
|
? prod.colors
|
|
: [
|
|
{
|
|
id: `color-${Date.now()}-${existingColors.length + 1}`,
|
|
colorName: prod.colorName || `Estilo #${existingColors.length + 1}`,
|
|
image: prod.images[0],
|
|
images: prod.images,
|
|
sizes: prod.sizes || ['37', '38', '39', '40', '41', '42', '43'],
|
|
inStock: prod.inStock !== false,
|
|
},
|
|
];
|
|
|
|
// Agregar colorways que no estén ya presentes (por URL de imagen)
|
|
incomingColors.forEach((inc) => {
|
|
const isDuplicate = existingColors.some(
|
|
(ec) => ec.image === inc.image || ec.colorName.toLowerCase() === inc.colorName.toLowerCase()
|
|
);
|
|
if (!isDuplicate) {
|
|
existingColors.push({
|
|
...inc,
|
|
id: inc.id || `color-${Date.now()}-${existingColors.length + 1}`,
|
|
});
|
|
}
|
|
});
|
|
|
|
// Fusionar imágenes sin duplicados
|
|
const mergedImages = Array.from(new Set([...existing.images, ...prod.images]));
|
|
|
|
// Fusionar tallas sin duplicados
|
|
const mergedSizes = Array.from(new Set([...existing.sizes, ...(prod.sizes || [])])).sort(
|
|
(a, b) => parseInt(a, 10) - parseInt(b, 10)
|
|
);
|
|
|
|
existing.colors = existingColors;
|
|
existing.images = mergedImages;
|
|
existing.sizes = mergedSizes;
|
|
}
|
|
}
|
|
|
|
return Array.from(modelMap.values());
|
|
}
|