From 10eda267fa9f9c9ebb9774ca0ebb36e97616da5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Mon, 10 Aug 2026 09:45:59 -0300 Subject: [PATCH] Simplify purchase need actions --- src/pages/Supplies.tsx | 180 ++++++++++++----------------------------- 1 file changed, 50 insertions(+), 130 deletions(-) diff --git a/src/pages/Supplies.tsx b/src/pages/Supplies.tsx index 75495b2..ac9963a 100644 --- a/src/pages/Supplies.tsx +++ b/src/pages/Supplies.tsx @@ -9,6 +9,7 @@ import { ClipboardCheck, ClipboardList, Download, + Eye, Link as LinkIcon, Package, PackageSearch, @@ -2099,26 +2100,55 @@ const getNeedDisplayName = (need: SupplyPurchaseNeed) => ( need.material.replace(/^Cadastrar (consumo|rendimento):\s*/i, '') ); -const getNeedPendingLabel = (need: SupplyPurchaseNeed) => { - if (!need.missingReference) return purchaseStatusLabels[need.status]; - return /^Cadastrar rendimento:/i.test(need.material) ? 'Rendimento' : 'Referência'; -}; - const hasNegativeMaterialBalance = (need: SupplyPurchaseNeed) => need.stockKg < 0; -type PurchaseNeedTab = 'materials' | 'services' | 'mapping'; +type PurchaseNeedTab = 'materials' | 'services'; type MaterialPurchaseFilter = 'to_buy' | 'negative_balance' | 'no_stock' | 'all'; const sumNeedProducts = ( need: SupplyPurchaseNeed, - field: 'suggestedQuantity' | 'quantitySold' | 'stockQuantity' + field: 'suggestedQuantity' ) => ( (need.products || []).reduce((total, product) => total + Number(product[field] || 0), 0) ); -const countImpactedSkus = (needs: SupplyPurchaseNeed[]) => ( - new Set(needs.flatMap(need => (need.products || []).map(product => product.productId).filter(Boolean))).size -); +const AffectedProducts = ({ products }: { products: SupplyPurchaseNeed['products'] }) => { + if (!products?.length) return null; + + return ( +
+ Produtos afetados + {products.slice(0, 3).map(product => ( +
+ + #{product.productId} · {product.name} + + + + + + + +
+ ))} + {products.length > 3 && +{products.length - 3} SKUs} +
+ ); +}; const PurchaseMaterialQueue = ({ needs }: { needs: SupplyPurchaseNeed[] }) => ( <> @@ -2192,22 +2222,7 @@ const PurchaseMaterialQueue = ({ needs }: { needs: SupplyPurchaseNeed[] }) => ( - {products.length > 0 && ( -
- Produtos afetados - {products.slice(0, 3).map(product => ( - - #{product.productId} · {product.name} - - ))} - {products.length > 3 && +{products.length - 3} SKUs} -
- )} + ); })} @@ -2264,85 +2279,7 @@ const PurchaseServiceQueue = ({ needs }: { needs: SupplyPurchaseNeed[] }) => ( - {products.length > 0 && ( -
- Produtos afetados - {products.slice(0, 3).map(product => ( - - #{product.productId} · {product.name} - - ))} - {products.length > 3 && +{products.length - 3} SKUs} -
- )} - - ); - })} - - -); - -const PurchaseMappingQueue = ({ needs }: { needs: SupplyPurchaseNeed[] }) => ( - <> -
-
- SKU sem composição confiável - Impacto antes de comprar - Correção -
-
-
- {needs.map(need => { - const referenceProduct = need.products?.[0]; - const productName = referenceProduct?.name || getNeedDisplayName(need); - const productId = referenceProduct?.productId || ''; - const soldQuantity = referenceProduct?.quantitySold ?? sumNeedProducts(need, 'quantitySold'); - const stockQuantity = referenceProduct?.stockQuantity ?? sumNeedProducts(need, 'stockQuantity'); - - return ( -
-
-
- {productId ? ( - {productName} - ) : ( -

{productName}

- )} -
- {productId ? `SKU ${productId}` : 'SKU não informado'} - {getNeedPendingLabel(need)} -
-
- -
-
-

Vendido

-

{formatNumber(soldQuantity, 0)} un.

-
-
-

Estoque produto

-

{formatNumber(stockQuantity, 0)} un.

-
-
-

Necessidade estimada

-

{formatNeedQuantity(need, need.purchaseKg)}

-
-
- -
- {referenceProduct ? ( - - - Mapear consumo - - ) : ( - SKU necessário para corrigir - )} -
-
+
); })} @@ -2357,7 +2294,7 @@ const PurchaseNeedsScreen = () => { const [search, setSearch] = useState(''); const requestedTab = searchParams.get('tab'); const requestedFilter = searchParams.get('filter'); - const activeTab: PurchaseNeedTab = requestedTab === 'services' || requestedTab === 'mapping' ? requestedTab : 'materials'; + const activeTab: PurchaseNeedTab = requestedTab === 'services' ? 'services' : 'materials'; const materialFilter: MaterialPurchaseFilter = requestedFilter === 'negative_balance' || requestedFilter === 'no_stock' || requestedFilter === 'all' ? requestedFilter : 'to_buy'; const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); @@ -2395,7 +2332,6 @@ const PurchaseNeedsScreen = () => { const materialNeeds = summary.purchaseNeeds.filter(need => !need.missingReference && !isServicePurchaseNeed(need)); const serviceNeeds = summary.purchaseNeeds.filter(need => !need.missingReference && isServicePurchaseNeed(need)); - const mappingNeeds = summary.purchaseNeeds.filter(need => need.missingReference); const purchaseNeeds = materialNeeds.filter(need => need.purchaseKg > 0); const negativeBalanceNeeds = materialNeeds.filter(hasNegativeMaterialBalance); const noStockNeeds = materialNeeds.filter(need => need.stockKg === 0 && need.purchaseKg > 0); @@ -2411,7 +2347,7 @@ const PurchaseNeedsScreen = () => { if (a.status !== b.status) return a.status === 'critical' ? -1 : b.status === 'critical' ? 1 : 0; return b.purchaseKg - a.purchaseKg; }) - : activeTab === 'services' ? serviceNeeds : mappingNeeds; + : serviceNeeds; const normalizedSearch = normalizeSearch(search); const visibleNeeds = activeNeeds.filter(need => ( !normalizedSearch || normalizeSearch(`${need.material} ${getNeedDisplayName(need)} ${need.suppliers.join(' ')} ${need.colors.join(' ')} ${(need.products || []).map(product => `${product.productId} ${product.name}`).join(' ')}`).includes(normalizedSearch) @@ -2422,9 +2358,8 @@ const PurchaseNeedsScreen = () => { const paginatedNeeds = visibleNeeds.slice(startIndex, startIndex + itemsPerPage); const purchaseItemCount = purchaseNeeds.length; const suggestedPurchaseSummary = summarizePurchaseNeeds(purchaseNeeds); - const impactedSkuCount = countImpactedSkus(mappingNeeds); const pageTitle = 'Necessidade de Compra'; - const pageSubtitle = 'Materiais, serviços terceirizados e pendências de cadastro em filas separadas.'; + const pageSubtitle = 'Materiais e serviços terceirizados para execução; pendências de cadastro ficam em Dados para revisar.'; const materialFilters: Array<{ id: MaterialPurchaseFilter; label: string; description: string; count: number }> = [ { id: 'to_buy', label: 'Comprar agora', description: 'Materiais com saldo insuficiente para a demanda.', count: purchaseItemCount }, { id: 'negative_balance', label: 'Saldo negativo', description: 'Divergências de estoque que precisam de conferência.', count: negativeBalanceNeeds.length }, @@ -2432,17 +2367,15 @@ const PurchaseNeedsScreen = () => { { id: 'all', label: 'Todos os materiais', description: 'Inclui itens cobertos para auditoria da conta.', count: materialNeeds.length }, ]; const activeMaterialFilter = materialFilters.find(filter => filter.id === materialFilter); - const panelTitle = activeTab === 'materials' ? activeMaterialFilter?.label || 'Compra de materiais' : activeTab === 'services' ? 'Serviços terceirizados' : 'Pendências de composição'; + const panelTitle = activeTab === 'materials' ? activeMaterialFilter?.label || 'Compra de materiais' : 'Serviços terceirizados'; const panelSubtitle = activeTab === 'materials' ? activeMaterialFilter?.description || 'Demanda de produto → material → saldo disponível → quantidade para comprar.' - : activeTab === 'services' - ? 'Necessidades de beneficiamento e serviços. Não entram no total de compra de materiais.' - : 'SKUs sem consumo confiável. Corrija o cadastro antes de usar estes valores para comprar.'; + : 'Necessidades de beneficiamento e serviços. Não entram no total de compra de materiais.'; return (
-
+
-
@@ -2488,7 +2412,6 @@ const PurchaseNeedsScreen = () => { type="button" onClick={() => exportCsv(`necessidade-${activeTab}.csv`, visibleNeeds.map(need => ({ material: getNeedDisplayName(need), - pendencia: need.missingReference ? getNeedPendingLabel(need) : '', planejado: need.plannedKg, estoque: need.stockKg, pendente: need.pendingKg, @@ -2511,7 +2434,6 @@ const PurchaseNeedsScreen = () => { {[ { id: 'materials' as const, label: `Compra (${purchaseItemCount})` }, { id: 'services' as const, label: `Serviços (${serviceNeeds.length})` }, - { id: 'mapping' as const, label: `Mapear dados (${mappingNeeds.length})` }, ].map(tab => (
@@ -2556,8 +2478,6 @@ const PurchaseNeedsScreen = () => {
{activeTab === 'materials' ? ( - ) : activeTab === 'mapping' ? ( - ) : ( )} @@ -2568,7 +2488,7 @@ const PurchaseNeedsScreen = () => { totalPages={totalPages} pageSize={itemsPerPage} pageSizeOptions={[10, 20, 50, 100]} - itemLabel={activeTab === 'materials' ? 'materiais' : activeTab === 'services' ? 'serviços' : 'pendências'} + itemLabel={activeTab === 'materials' ? 'materiais' : 'serviços'} pageSizeLabel="itens por página" startIndex={startIndex} endIndex={Math.min(startIndex + itemsPerPage, visibleNeeds.length)}