From d3d8886a900fcbfb9dfaf9c001a98fa865220236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Thu, 9 Jul 2026 13:58:48 -0300 Subject: [PATCH] Format product color labels --- src/components/ProductColorBadge.tsx | 31 ++++------------------------ src/displayFormatters.ts | 15 ++++++++++++++ src/pages/ProductDetails.tsx | 3 ++- src/pages/ProductGroupDetails.tsx | 9 +++++--- src/pages/Products.tsx | 3 ++- src/pages/Replenishment.tsx | 3 ++- src/productColors.ts | 26 +++++++++++++++++++++++ 7 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 src/productColors.ts diff --git a/src/components/ProductColorBadge.tsx b/src/components/ProductColorBadge.tsx index ef02606..2fb2561 100644 --- a/src/components/ProductColorBadge.tsx +++ b/src/components/ProductColorBadge.tsx @@ -1,29 +1,5 @@ -const COLOR_SWATCHES: Array<{ pattern: string; color: string }> = [ - { pattern: 'preto', color: '#171717' }, - { pattern: 'branco', color: '#f8fafc' }, - { pattern: 'bege', color: '#d7bf9a' }, - { pattern: 'cafe', color: '#79553d' }, - { pattern: 'perola', color: '#e7dfcf' }, - { pattern: 'marinho', color: '#172554' }, - { pattern: 'bordo', color: '#6b1226' }, - { pattern: 'verde', color: '#166534' }, - { pattern: 'rosa', color: '#f0a6bf' }, - { pattern: 'cinza', color: '#8f8f8f' }, - { pattern: 'vermelho', color: '#b91c1c' }, - { pattern: 'grafite', color: '#3f3f46' }, - { pattern: 'azul', color: '#2563eb' }, - { pattern: 'marron', color: '#6b4f3b' }, - { pattern: 'marrom', color: '#6b4f3b' } -]; - -const normalizeColorLabel = (label: string) => ( - label.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase() -); - -export const getProductColor = (label: string) => { - const normalizedLabel = normalizeColorLabel(label); - return COLOR_SWATCHES.find(item => normalizedLabel.includes(item.pattern))?.color || '#64748b'; -}; +import { formatColorLabel } from '../displayFormatters'; +import { getProductColor } from '../productColors'; export const ProductColorSwatch = ({ label, @@ -50,7 +26,8 @@ const ProductColorBadge = ({ emptyLabel?: string; className?: string; }) => { - const displayLabel = label?.trim() || emptyLabel; + const normalizedLabel = label?.trim(); + const displayLabel = normalizedLabel ? formatColorLabel(normalizedLabel) : emptyLabel; return ( diff --git a/src/displayFormatters.ts b/src/displayFormatters.ts index 3521509..b693a57 100644 --- a/src/displayFormatters.ts +++ b/src/displayFormatters.ts @@ -24,3 +24,18 @@ export const formatDisplayName = (value: string) => { }) .join(' '); }; + +export const formatColorLabel = (value: string) => { + const normalized = String(value || '').replace(/\s+/g, ' ').trim(); + if (!normalized) return ''; + if (normalized.toLocaleLowerCase('pt-BR') === 'sem cor') return 'Sem cor'; + + return normalized + .toLocaleLowerCase('pt-BR') + .split(' ') + .map((word, index) => { + if (index > 0 && SMALL_WORDS.has(word)) return word; + return word.charAt(0).toLocaleUpperCase('pt-BR') + word.slice(1); + }) + .join(' '); +}; diff --git a/src/pages/ProductDetails.tsx b/src/pages/ProductDetails.tsx index eaf9cb5..c4e89fb 100644 --- a/src/pages/ProductDetails.tsx +++ b/src/pages/ProductDetails.tsx @@ -8,6 +8,7 @@ import RefreshStatus from '../components/RefreshStatus'; import type { DateRange, ProductDetailsAnalytics } from '../types'; import { fetchProductDetailsAnalytics } from '../dataService'; import { parseProductName } from '../productParsing'; +import { formatColorLabel } from '../displayFormatters'; const CHART_GRID_COLOR = 'var(--chart-grid)'; const CHART_AXIS_COLOR = 'var(--chart-axis)'; @@ -361,7 +362,7 @@ const ProductDetails = () => { #{variant.id} {metadata.color && ( - {metadata.color} + {formatColorLabel(metadata.color)} )} {metadata.size && ( diff --git a/src/pages/ProductGroupDetails.tsx b/src/pages/ProductGroupDetails.tsx index 7152865..9fd6aae 100644 --- a/src/pages/ProductGroupDetails.tsx +++ b/src/pages/ProductGroupDetails.tsx @@ -4,11 +4,13 @@ import { DollarSign, Package, Palette, Ruler, TrendingDown, TrendingUp, Warehous import BackButton from '../components/BackButton'; import DateRangePicker from '../components/DateRangePicker'; import PaginationControls from '../components/PaginationControls'; -import ProductColorBadge, { ProductColorSwatch, getProductColor } from '../components/ProductColorBadge'; +import ProductColorBadge, { ProductColorSwatch } from '../components/ProductColorBadge'; import RefreshStatus from '../components/RefreshStatus'; import { buildOpenProductionByProductId } from '../analytics/cutting'; import { fetchProductAnalytics, fetchProductionOrders } from '../dataService'; import { decodeProductGroupKey, normalizeProductText, parseProductName } from '../productParsing'; +import { formatColorLabel } from '../displayFormatters'; +import { getProductColor } from '../productColors'; import type { DateRange, ProductAnalyticsItem, ProductionOrderItem } from '../types'; type VariantRow = ProductAnalyticsItem & { @@ -121,13 +123,14 @@ const BreakdownPanel = ({ {visibleRows.map(row => { const width = maxSold ? Math.max(4, (row.quantitySold / maxSold) * 100) : 0; const barColor = type === 'color' ? getBarColor(row.label) : '#25c2ff'; + const displayLabel = type === 'color' ? formatColorLabel(row.label) : row.label; return (
{type === 'color' ? : } - - {type === 'size' && row.label !== 'Sem tamanho' ? `Tam. ${row.label}` : row.label} + + {type === 'size' && row.label !== 'Sem tamanho' ? `Tam. ${row.label}` : displayLabel}
diff --git a/src/pages/Products.tsx b/src/pages/Products.tsx index 17f43bf..f9d92ff 100644 --- a/src/pages/Products.tsx +++ b/src/pages/Products.tsx @@ -7,6 +7,7 @@ import RefreshStatus from '../components/RefreshStatus'; import type { DateRange, ProductAnalyticsItem } from '../types'; import { exportToCSV, fetchProductAnalytics } from '../dataService'; import { encodeProductGroupKey, parseProductName, sortProductSizes } from '../productParsing'; +import { formatColorLabel } from '../displayFormatters'; type StockRisk = 'rupture' | 'critical' | 'attention' | 'monitor' | 'healthy' | 'no_sales'; type StockStatusFilter = 'all' | StockRisk; @@ -657,7 +658,7 @@ const Products = () => {
{product.name}
{viewMode === 'group' - ? `Cor principal: ${product.topColor} · Tam. principal: ${product.topSize} · ${product.colors.length} cores · ${product.sizes.length} tamanhos` + ? `Cor principal: ${formatColorLabel(product.topColor)} · Tam. principal: ${product.topSize} · ${product.colors.length} cores · ${product.sizes.length} tamanhos` : `Preço Atual: ${formatCurrency(product.lastPrice)}`}
diff --git a/src/pages/Replenishment.tsx b/src/pages/Replenishment.tsx index 07141ad..7a286a1 100644 --- a/src/pages/Replenishment.tsx +++ b/src/pages/Replenishment.tsx @@ -8,6 +8,7 @@ import { buildOpenProductionByProductId } from '../analytics/cutting'; import type { DateRange, ProductAnalyticsItem, ProductionOrderItem } from '../types'; import { exportToCSV, fetchProductAnalytics, fetchProductionOrders } from '../dataService'; import { encodeProductGroupKey, parseProductName, sortProductSizes } from '../productParsing'; +import { formatColorLabel } from '../displayFormatters'; type ReplenishmentStatus = 'need' | 'covered' | 'no_sales' | 'no_stock'; type ReplenishmentFilter = 'all' | ReplenishmentStatus; @@ -237,7 +238,7 @@ const Replenishment = () => { : 'covered'; const sizes = sortProductSizes(Array.from(new Set(group.flatMap(row => row.sizes)))); const productIds = group.map(row => row.id); - const name = first.color ? `${first.baseName} · ${first.color}` : first.baseName; + const name = first.color ? `${first.baseName} · ${formatColorLabel(first.color)}` : first.baseName; return { ...first, diff --git a/src/productColors.ts b/src/productColors.ts new file mode 100644 index 0000000..640be63 --- /dev/null +++ b/src/productColors.ts @@ -0,0 +1,26 @@ +const COLOR_SWATCHES: Array<{ pattern: string; color: string }> = [ + { pattern: 'preto', color: '#171717' }, + { pattern: 'branco', color: '#f8fafc' }, + { pattern: 'bege', color: '#d7bf9a' }, + { pattern: 'cafe', color: '#79553d' }, + { pattern: 'perola', color: '#e7dfcf' }, + { pattern: 'marinho', color: '#172554' }, + { pattern: 'bordo', color: '#6b1226' }, + { pattern: 'verde', color: '#166534' }, + { pattern: 'rosa', color: '#f0a6bf' }, + { pattern: 'cinza', color: '#8f8f8f' }, + { pattern: 'vermelho', color: '#b91c1c' }, + { pattern: 'grafite', color: '#3f3f46' }, + { pattern: 'azul', color: '#2563eb' }, + { pattern: 'marron', color: '#6b4f3b' }, + { pattern: 'marrom', color: '#6b4f3b' } +]; + +const normalizeColorLabel = (label: string) => ( + label.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase() +); + +export const getProductColor = (label: string) => { + const normalizedLabel = normalizeColorLabel(label); + return COLOR_SWATCHES.find(item => normalizedLabel.includes(item.pattern))?.color || '#64748b'; +};