From e1f001b57d48342838160116bedcfe7422506ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Mon, 20 Jul 2026 14:12:55 -0300 Subject: [PATCH] Add production order generation preview --- src/pages/Cutting.tsx | 154 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 6 deletions(-) diff --git a/src/pages/Cutting.tsx b/src/pages/Cutting.tsx index 2e1bd31..52de577 100644 --- a/src/pages/Cutting.tsx +++ b/src/pages/Cutting.tsx @@ -143,6 +143,7 @@ const Cutting = () => { const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); const [isGeneratingOrders, setIsGeneratingOrders] = useState(false); + const [isOrderPreviewOpen, setIsOrderPreviewOpen] = useState(false); const [generationMessage, setGenerationMessage] = useState(''); const cutConfigSection = searchParams.get('config'); const targetCorrectionSku = (searchParams.get('sku') || '').trim(); @@ -254,6 +255,10 @@ const Cutting = () => { filteredRows.filter(row => row.suggestedCutQuantity > 0) ), [filteredRows]); + const orderPreviewTotalUnits = useMemo(() => ( + rowsAvailableForOrder.reduce((total, row) => total + row.suggestedCutQuantity, 0) + ), [rowsAvailableForOrder]); + const totalPages = Math.ceil(filteredRows.length / itemsPerPage); const safeCurrentPage = Math.min(currentPage, totalPages || 1); const startIndex = (safeCurrentPage - 1) * itemsPerPage; @@ -302,11 +307,15 @@ const Cutting = () => { }, [correctionItemsPerPage, correctionRows, cutConfigSection, targetCorrectionSku]); useEffect(() => { - if (!isSettingsOpen) return undefined; + if (!isSettingsOpen && !isOrderPreviewOpen) return undefined; const closeOnEscape = (event: KeyboardEvent) => { if (event.key === 'Escape') { - setIsSettingsOpen(false); + if (isOrderPreviewOpen) { + setIsOrderPreviewOpen(false); + } else { + setIsSettingsOpen(false); + } } }; @@ -317,7 +326,7 @@ const Cutting = () => { document.body.style.overflow = previousOverflow; document.removeEventListener('keydown', closeOnEscape); }; - }, [isSettingsOpen]); + }, [isOrderPreviewOpen, isSettingsOpen]); const updateFamilyYield = (familyKey: CutFamilyKey, value: string) => { const parsedValue = Number(value); @@ -413,6 +422,16 @@ const Cutting = () => { })), `plano_corte_${new Date().toISOString().split('T')[0]}.csv`); }; + const openProductionOrderPreview = () => { + if (!rowsAvailableForOrder.length) { + setGenerationMessage('Não há necessidade de corte no filtro atual.'); + return; + } + + setGenerationMessage(''); + setIsOrderPreviewOpen(true); + }; + const generateProductionOrders = async () => { if (!rowsAvailableForOrder.length) { setGenerationMessage('Não há necessidade de corte no filtro atual.'); @@ -460,6 +479,7 @@ const Cutting = () => { const refreshedOrders = await fetchProductionOrders(allProductionOrdersRange, undefined, { force: true }); setProductionOrders(refreshedOrders.orders); setGenerationMessage(`${result.created.length} OPs criadas${result.skipped.length ? ` · ${result.skipped.length} já existiam abertas` : ''}.`); + setIsOrderPreviewOpen(false); } catch (error) { setGenerationMessage(error instanceof Error ? error.message : 'Não foi possível gerar as ordens de produção.'); } finally { @@ -513,13 +533,13 @@ const Cutting = () => { + + +
+
+

OPs previstas

+

{formatNumber(rowsAvailableForOrder.length)}

+
+
+

Unidades totais

+

{formatNumber(orderPreviewTotalUnits)} un.

+
+
+

Cobertura alvo

+

{formatNumber(targetCoverageDays)} dias

+
+
+ +
+ + + + + + + + + + + + + {rowsAvailableForOrder.map(row => ( + + + + + + + + + ))} + +
SKUProdutoFamíliaVariaçãoNecessidadeDados
#{row.id} +
{row.name}
+
+ Média: {formatNumber(row.dailySales, 2)} un./dia · Estoque: {formatNumber(row.stock)} un. +
+
+ + {row.family.materialLabel} + + +
+ + + + {row.size || '-'} + +
+
+ {formatNumber(row.suggestedCutQuantity)} un. + {renderIssueBadge(row)}
+
+ +
+

+ OPs abertas já existentes para o mesmo plano e SKU serão ignoradas. +

+
+ + +
+
+ + + )} + {isSettingsOpen && (