From 63efb47e772423eb875bd6da2f3007a71aefb6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Tue, 14 Jul 2026 13:24:25 -0300 Subject: [PATCH] Route SKU actions to focused editors --- src/catalogLinks.ts | 12 ++++++ src/pages/Cutting.tsx | 44 ++++++++++++++++---- src/pages/Registrations.tsx | 22 ++++++++-- src/pages/Supplies.tsx | 81 ++++++++++++++++++++++--------------- 4 files changed, 116 insertions(+), 43 deletions(-) diff --git a/src/catalogLinks.ts b/src/catalogLinks.ts index c5150a5..2c516d1 100644 --- a/src/catalogLinks.ts +++ b/src/catalogLinks.ts @@ -12,3 +12,15 @@ export const buildSkuEditPath = ({ sku, name = '', color = '', size = '' }: SkuE if (size) params.set('size', size); return `/registrations?${params.toString()}`; }; + +export const buildCuttingSkuConfigPath = ({ sku }: Pick) => { + const params = new URLSearchParams({ config: 'corrections', sku }); + return `/cutting?${params.toString()}`; +}; + +export const buildConsumptionReferencePath = ({ sku, name = '', color = '' }: Omit) => { + const params = new URLSearchParams({ tab: 'references', sku }); + if (name) params.set('name', name); + if (color) params.set('color', color); + return `/registrations?${params.toString()}`; +}; diff --git a/src/pages/Cutting.tsx b/src/pages/Cutting.tsx index 4993e58..5a2c815 100644 --- a/src/pages/Cutting.tsx +++ b/src/pages/Cutting.tsx @@ -1,11 +1,11 @@ import { useEffect, useMemo, useState } from 'react'; -import { Link, useOutletContext } from 'react-router-dom'; +import { Link, useOutletContext, useSearchParams } from 'react-router-dom'; import { AlertTriangle, ArrowLeft, ClipboardList, Download, Eye, Layers3, Palette, Pencil, RotateCcw, Ruler, Save as SaveIcon, Scissors, Search, Settings2, X } from 'lucide-react'; import DateRangePicker from '../components/DateRangePicker'; import PaginationControls from '../components/PaginationControls'; import ProductColorBadge from '../components/ProductColorBadge'; import RefreshStatus from '../components/RefreshStatus'; -import { buildSkuEditPath } from '../catalogLinks'; +import { buildCuttingSkuConfigPath } from '../catalogLinks'; import { CUT_FAMILY_RULES, buildCutPlan, buildOpenProductionByProductId, type CutFamilyKey, type CutIssue, type CutPlanSkuRow, type CutProductOverride } from '../analytics/cutting'; import { exportToCSV, fetchCuttingSettings, fetchProductAnalytics, fetchProductionOrders, saveCuttingSettings } from '../dataService'; import type { CuttingSettings, DateRange, ProductAnalyticsItem, ProductionOrderItem } from '../types'; @@ -117,6 +117,7 @@ const Cutting = () => { dateRange: DateRange, setDateRange: (range: DateRange) => void }>(); + const [searchParams] = useSearchParams(); const [products, setProducts] = useState([]); const [productionOrders, setProductionOrders] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -134,6 +135,8 @@ const Cutting = () => { const [correctionPage, setCorrectionPage] = useState(1); const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); + const cutConfigSection = searchParams.get('config'); + const targetCorrectionSku = (searchParams.get('sku') || '').trim(); useEffect(() => { let isMounted = true; @@ -249,13 +252,20 @@ const Cutting = () => { (correctionIssueFilter === 'all' || row.issues.includes(correctionIssueFilter)) )); - return [...rows].sort((a, b) => { + const sortedRows = [...rows].sort((a, b) => { if (b.suggestedCutQuantity !== a.suggestedCutQuantity) { return b.suggestedCutQuantity - a.suggestedCutQuantity; } return a.name.localeCompare(b.name, 'pt-BR'); }); - }, [correctionIssueFilter, cutPlan.rows]); + + if (!targetCorrectionSku) return sortedRows; + + const targetRow = cutPlan.rows.find(row => row.id.toLowerCase() === targetCorrectionSku.toLowerCase()); + if (!targetRow) return sortedRows; + + return [targetRow, ...sortedRows.filter(row => row.id !== targetRow.id)]; + }, [correctionIssueFilter, cutPlan.rows, targetCorrectionSku]); const correctionItemsPerPage = 12; const correctionTotalPages = Math.ceil(correctionRows.length / correctionItemsPerPage); const safeCorrectionPage = Math.min(correctionPage, correctionTotalPages || 1); @@ -264,6 +274,20 @@ const Cutting = () => { const configuredYieldCount = CUT_FAMILY_RULES.filter(rule => cuttingSettings.familyYields[rule.key]).length; const productOverrideCount = Object.keys(cuttingSettings.productOverrides).length; + useEffect(() => { + if (cutConfigSection !== 'corrections' || !targetCorrectionSku) return; + + const correctionIndex = correctionRows.findIndex(row => row.id.toLowerCase() === targetCorrectionSku.toLowerCase()); + queueMicrotask(() => { + setIsSettingsOpen(true); + setSettingsSection('corrections'); + setCorrectionIssueFilter('all'); + if (correctionIndex >= 0) { + setCorrectionPage(Math.floor(correctionIndex / correctionItemsPerPage) + 1); + } + }); + }, [correctionItemsPerPage, correctionRows, cutConfigSection, targetCorrectionSku]); + useEffect(() => { if (!isSettingsOpen) return undefined; @@ -586,8 +610,12 @@ const Cutting = () => {
{paginatedCorrectionRows.map(row => { const override = cuttingSettings.productOverrides[row.id] || {}; + const isTargetRow = targetCorrectionSku.toLowerCase() === row.id.toLowerCase(); return ( -
+
#{row.id}
{row.name}
@@ -948,10 +976,10 @@ const Cutting = () => {
diff --git a/src/pages/Registrations.tsx b/src/pages/Registrations.tsx index 9c4b361..2daf6b5 100644 --- a/src/pages/Registrations.tsx +++ b/src/pages/Registrations.tsx @@ -146,7 +146,7 @@ const Registrations = () => { const sku = (searchParams.get('sku') || '').trim(); if (!sku && !(requestedTab === 'products' || requestedTab === 'categories' || requestedTab === 'references')) return; - const prefillKey = `${sku}|${searchParams.get('name') || ''}|${catalog.products.length}`; + const prefillKey = `${requestedTab || ''}|${sku}|${searchParams.get('name') || ''}|${searchParams.get('color') || ''}|${catalog.products.length}`; if (appliedSkuPrefillRef.current === prefillKey) return; appliedSkuPrefillRef.current = prefillKey; @@ -158,10 +158,22 @@ const Registrations = () => { if (!sku) return; const existingProduct = catalog.products.find(product => product.sku.toLowerCase() === sku.toLowerCase()); - setActiveTab('products'); setProductFilter('all'); setStatus('idle'); + if (requestedTab === 'references' && existingProduct) { + setActiveTab('references'); + setReferenceForm(current => ({ + ...current, + productId: String(existingProduct.id), + color: searchParams.get('color') || existingProduct.color || current.color + })); + setFeedback(`Criando referência de consumo para SKU ${existingProduct.sku}.`); + return; + } + + setActiveTab('products'); + if (existingProduct) { setProductForm({ type: existingProduct.type, @@ -189,7 +201,11 @@ const Registrations = () => { color: searchParams.get('color') || '', sizes: requestedSize ? [requestedSize] : defaultProductForm.sizes }); - setFeedback(`Novo cadastro para SKU ${sku}.`); + setFeedback( + requestedTab === 'references' + ? `Cadastre o SKU ${sku} antes de criar a referência de consumo.` + : `Novo cadastro para SKU ${sku}.` + ); }); }, [catalog.products, searchParams]); diff --git a/src/pages/Supplies.tsx b/src/pages/Supplies.tsx index 5c074b6..601cc49 100644 --- a/src/pages/Supplies.tsx +++ b/src/pages/Supplies.tsx @@ -11,6 +11,7 @@ import { Download, Link as LinkIcon, Package, + Pencil, RefreshCw, Repeat2, Ruler, @@ -21,6 +22,7 @@ import { Trash2, Warehouse, } from 'lucide-react'; +import { buildConsumptionReferencePath } from '../catalogLinks'; import { adjustSupplyLotInventory, approveSupplyReceipt, consumeSupplyLotForProduction, createSupplyFabricPlan, createSupplyReceipt, deleteSupplyFabricPlan, deleteSupplyReceipt, fetchSupplySummary } from '../dataService'; import type { SupplyFabricPlan, SupplyLot, SupplyMovement, SupplyPurchaseNeed, SupplyReceipt, SupplySummary } from '../types'; @@ -1246,39 +1248,54 @@ const PurchaseNeedsScreen = () => { Cobertura
- {visibleNeeds.map(need => ( -
-
-

{need.material}

-

- {need.missingReference - ? 'Cadastre produto/material em Cadastros > Referência de Consumo' - : `${(need.colors.length ? need.colors.join(', ') : 'Todas as cores')} · ${(need.suppliers.length ? need.suppliers.join(', ') : 'Sem fornecedor')}`} -

- {need.products?.length ? ( -

- {need.products.slice(0, 2).map(product => product.productId).join(', ')} - {need.products.length > 2 ? ` +${need.products.length - 2}` : ''} -

- ) : null} + {visibleNeeds.map(need => { + const referenceProduct = need.products?.[0]; + return ( +
+
+

{need.material}

+
+

+ {need.missingReference + ? 'Cadastre produto/material em Cadastros > Referência de Consumo' + : `${(need.colors.length ? need.colors.join(', ') : 'Todas as cores')} · ${(need.suppliers.length ? need.suppliers.join(', ') : 'Sem fornecedor')}`} +

+ {need.missingReference && referenceProduct ? ( + + + + ) : null} +
+ {need.products?.length ? ( +

+ {need.products.slice(0, 2).map(product => product.productId).join(', ')} + {need.products.length > 2 ? ` +${need.products.length - 2}` : ''} +

+ ) : null} +
+

{formatNeedQuantity(need, need.plannedKg)}

+

{formatNeedQuantity(need, need.stockKg)}

+

{formatNeedQuantity(need, need.pendingKg)}

+

0 ? 'text-red-300' : 'text-emerald-300'}`}>{formatNeedQuantity(need, need.purchaseKg)}

+ + {need.missingReference ? 'Sem referência' : purchaseStatusLabels[need.status]} +
-

{formatNeedQuantity(need, need.plannedKg)}

-

{formatNeedQuantity(need, need.stockKg)}

-

{formatNeedQuantity(need, need.pendingKg)}

-

0 ? 'text-red-300' : 'text-emerald-300'}`}>{formatNeedQuantity(need, need.purchaseKg)}

- - {need.missingReference ? 'Sem referência' : purchaseStatusLabels[need.status]} - -
- ))} + ); + })}
) : (