Improve planning data normalization
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m25s

This commit is contained in:
Cauê Faleiros
2026-07-20 11:24:47 -03:00
parent 3066ba06a2
commit 9af5f296a7
9 changed files with 112 additions and 18 deletions

View File

@@ -1,8 +1,54 @@
const sizeOrder = ['2', '4', '6', '8', '10', '12', '14', '16', 'PP', 'P', 'M', 'G', 'GG', 'XG', 'G1', 'G2', 'G3', 'G4', 'G5'];
const sizeSet = new Set(sizeOrder);
const knownColors = [
'BRANCO + PRETO',
'CINZA/PRETO',
'VERDE BANDEIRA',
'VERDE MILITAR',
'AZUL MARINHO',
'CINZA GRAFITE',
'AMARELO',
'BRANCO',
'GRAFITE',
'MARINHO',
'VERMELHO',
'BORDO',
'CAFE',
'CAQUI',
'CHUMBO',
'CINZA',
'MARROM',
'PEROLA',
'PRETO',
'ROYAL',
'ROSA',
'ROXO',
'VERDE',
'AZUL',
'BEGE'
].sort((a, b) => b.length - a.length);
export const normalizeProductText = (value: string) => value.replace(/\s+/g, ' ').trim();
const normalizeForMatch = (value: string) => normalizeProductText(value)
.normalize('NFD')
.replace(/\p{Diacritic}/gu, '')
.toUpperCase();
const escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const toFlexiblePattern = (value: string) => escapeRegex(value).replace(/\s+/g, '\\s+');
const findLeadingColor = (value: string) => {
const normalizedValue = normalizeForMatch(value);
return knownColors.find(color => normalizedValue === color || normalizedValue.startsWith(`${color} `)) || '';
};
const findTrailingColor = (value: string) => {
const normalizedValue = normalizeForMatch(value);
return knownColors.find(color => normalizedValue === color || normalizedValue.endsWith(` ${color}`) || normalizedValue.endsWith(`- ${color}`)) || '';
};
export const encodeProductGroupKey = (value: string) => {
const normalizedValue = normalizeProductText(value);
const bytes = new TextEncoder().encode(normalizedValue);
@@ -34,16 +80,51 @@ export const parseProductName = (name: string) => {
const explicitSizeMatch = cleanName.match(/\bTAMANHO\s*-?\s*([A-Z0-9]+)\b/i);
const trailingTokenMatch = cleanName.match(/(?:\s+-\s+|\s)([A-Z0-9]+)$/i);
const trailingToken = trailingTokenMatch?.[1]?.toUpperCase() || '';
const size = (explicitSizeMatch?.[1] || (sizeSet.has(trailingToken) ? trailingToken : '')).toUpperCase();
let size = (explicitSizeMatch?.[1] || (sizeSet.has(trailingToken) ? trailingToken : '')).toUpperCase();
const colorMatch = cleanName.match(/\bCOR\s+(.+?)(?:\s+TAMANHO|\s+-\s+[A-Z0-9]+$|$)/i);
const color = normalizeProductText(colorMatch?.[1] || '');
let color = normalizeProductText(findLeadingColor(colorMatch?.[1] || '') || colorMatch?.[1] || '');
if (!color && size) {
const nameBeforeSize = cleanName.replace(new RegExp(`(?:\\s+-\\s+|\\s)${escapeRegex(size)}$`, 'i'), '');
color = findTrailingColor(nameBeforeSize);
}
if (!color) {
const trailingColor = findTrailingColor(cleanName);
if (trailingColor) {
const normalizedName = normalizeForMatch(cleanName);
const normalizedPrefix = normalizeProductText(normalizedName.replace(new RegExp(`${escapeRegex(trailingColor)}$`), ''));
const tokenBeforeColor = normalizedPrefix.match(/([A-Z0-9]+)$/)?.[1] || '';
if (sizeSet.has(tokenBeforeColor)) {
color = trailingColor;
size = tokenBeforeColor;
}
}
}
let baseName = cleanName
.replace(/\bCOR\s+.+?(?:\s+TAMANHO\s*-?\s*[A-Z0-9]+|\s+-\s+[A-Z0-9]+$|$)/i, '')
.replace(/\bTAMANHO\s*-?\s*[A-Z0-9]+\b/i, '')
.replace(/\s+-\s*[A-Z0-9]+$/i, '');
if (color && size) {
const colorPattern = toFlexiblePattern(color);
const sizePattern = escapeRegex(size);
baseName = baseName
.replace(new RegExp(`\\s+-\\s*${colorPattern}\\s+${sizePattern}$`, 'i'), '')
.replace(new RegExp(`\\s+${colorPattern}\\s+-\\s*${sizePattern}$`, 'i'), '')
.replace(new RegExp(`\\s+${sizePattern}\\s+${colorPattern}$`, 'i'), '')
.replace(new RegExp(`\\s+${colorPattern}\\s+${sizePattern}$`, 'i'), '');
}
if (color) {
const colorPattern = toFlexiblePattern(color);
baseName = baseName
.replace(new RegExp(`\\s+-\\s*${colorPattern}$`, 'i'), '')
.replace(new RegExp(`\\s+${colorPattern}$`, 'i'), '');
}
baseName = normalizeProductText(baseName.replace(/\s+-\s*$/g, ''));
return {