diff --git a/src/catalogLinks.ts b/src/catalogLinks.ts new file mode 100644 index 0000000..c5150a5 --- /dev/null +++ b/src/catalogLinks.ts @@ -0,0 +1,14 @@ +type SkuEditParams = { + sku: string; + name?: string; + color?: string; + size?: string; +}; + +export const buildSkuEditPath = ({ sku, name = '', color = '', size = '' }: SkuEditParams) => { + const params = new URLSearchParams({ tab: 'products', sku }); + if (name) params.set('name', name); + if (color) params.set('color', color); + if (size) params.set('size', size); + return `/registrations?${params.toString()}`; +}; diff --git a/src/pages/Cutting.tsx b/src/pages/Cutting.tsx index 1f952c1..4993e58 100644 --- a/src/pages/Cutting.tsx +++ b/src/pages/Cutting.tsx @@ -1,10 +1,11 @@ import { useEffect, useMemo, useState } from 'react'; import { Link, useOutletContext } from 'react-router-dom'; -import { AlertTriangle, ArrowLeft, ClipboardList, Download, Layers3, Package, Palette, RotateCcw, Ruler, Save as SaveIcon, Scissors, Search, Settings2, X } from 'lucide-react'; +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 { 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'; @@ -945,13 +946,24 @@ const Cutting = () => { {renderIssueBadge(row)} - - - Ver SKU - +
+ + + + + + +
))} diff --git a/src/pages/ProductGroupDetails.tsx b/src/pages/ProductGroupDetails.tsx index 9fd6aae..f03843a 100644 --- a/src/pages/ProductGroupDetails.tsx +++ b/src/pages/ProductGroupDetails.tsx @@ -1,11 +1,12 @@ import { useEffect, useMemo, useState } from 'react'; import { Link, useOutletContext, useParams } from 'react-router-dom'; -import { DollarSign, Package, Palette, Ruler, TrendingDown, TrendingUp, Warehouse } from 'lucide-react'; +import { DollarSign, Eye, Package, Palette, Pencil, Ruler, TrendingDown, Warehouse } from 'lucide-react'; import BackButton from '../components/BackButton'; import DateRangePicker from '../components/DateRangePicker'; import PaginationControls from '../components/PaginationControls'; import ProductColorBadge, { ProductColorSwatch } from '../components/ProductColorBadge'; import RefreshStatus from '../components/RefreshStatus'; +import { buildSkuEditPath } from '../catalogLinks'; import { buildOpenProductionByProductId } from '../analytics/cutting'; import { fetchProductAnalytics, fetchProductionOrders } from '../dataService'; import { decodeProductGroupKey, normalizeProductText, parseProductName } from '../productParsing'; @@ -541,13 +542,24 @@ const ProductGroupDetails = () => { {formatNumber(row.dailySales, 2)} un./dia {formatCurrency(row.revenue)} - - - Ver SKU - +
+ + + + + + +
))} diff --git a/src/pages/Products.tsx b/src/pages/Products.tsx index f9d92ff..07e9772 100644 --- a/src/pages/Products.tsx +++ b/src/pages/Products.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { Link, useOutletContext } from 'react-router-dom'; -import { Download, Filter, Package, PackageCheck, Search, TrendingDown, TrendingUp, X } from 'lucide-react'; +import { Download, Eye, Filter, Package, PackageCheck, Pencil, Search, TrendingDown, X } from 'lucide-react'; +import { buildSkuEditPath } from '../catalogLinks'; import DateRangePicker from '../components/DateRangePicker'; import PaginationControls from '../components/PaginationControls'; import RefreshStatus from '../components/RefreshStatus'; @@ -630,7 +631,7 @@ const Products = () => { - + @@ -684,13 +685,26 @@ const Products = () => { {formatCurrency(product.revenue)} - - - {viewMode === 'group' ? 'Ver Grupo' : 'Ver Gráfico'} - +
+ {viewMode === 'sku' && ( + + + + )} + + + +
); diff --git a/src/pages/Registrations.tsx b/src/pages/Registrations.tsx index 87b88ab..9c4b361 100644 --- a/src/pages/Registrations.tsx +++ b/src/pages/Registrations.tsx @@ -1,4 +1,5 @@ -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { Loader2, Package, RefreshCw, Ruler, Save, Tags, Trash2 } from 'lucide-react'; import { deleteCatalogCategory, @@ -35,6 +36,21 @@ const defaultSizeAreas: Record = { G5: '1.40' }; +const defaultProductForm = { + type: 'finished_product' as CatalogProductType, + sku: '', + name: '', + categoryId: '', + composition: '', + notes: '', + gramature: '', + materialYield: '', + widthCm: '', + color: '', + subcategory: 'malha', + sizes: ['P', 'M', 'G', 'GG'] as string[] +}; + const rawMaterialSubcategories = [ { value: 'fio', label: 'Fio' }, { value: 'malha', label: 'Malha' }, @@ -77,28 +93,17 @@ const getAverageYield = (reference: ConsumptionReference) => { }; const Registrations = () => { + const [searchParams] = useSearchParams(); const [catalog, setCatalog] = useState(emptyCatalog); const [isLoading, setIsLoading] = useState(true); const [activeTab, setActiveTab] = useState('products'); const [productFilter, setProductFilter] = useState<'all' | CatalogProductType>('all'); const [status, setStatus] = useState('idle'); const [feedback, setFeedback] = useState(''); + const appliedSkuPrefillRef = useRef(''); const [categoryForm, setCategoryForm] = useState({ name: '', description: '' }); - const [productForm, setProductForm] = useState({ - type: 'finished_product' as CatalogProductType, - sku: '', - name: '', - categoryId: '', - composition: '', - notes: '', - gramature: '', - materialYield: '', - widthCm: '', - color: '', - subcategory: 'malha', - sizes: ['P', 'M', 'G', 'GG'] as string[] - }); + const [productForm, setProductForm] = useState(defaultProductForm); const [referenceForm, setReferenceForm] = useState({ productId: '', materialProductId: '', @@ -136,6 +141,58 @@ const Registrations = () => { }; }, []); + useEffect(() => { + const requestedTab = searchParams.get('tab'); + const sku = (searchParams.get('sku') || '').trim(); + if (!sku && !(requestedTab === 'products' || requestedTab === 'categories' || requestedTab === 'references')) return; + + const prefillKey = `${sku}|${searchParams.get('name') || ''}|${catalog.products.length}`; + if (appliedSkuPrefillRef.current === prefillKey) return; + appliedSkuPrefillRef.current = prefillKey; + + queueMicrotask(() => { + if (requestedTab === 'products' || requestedTab === 'categories' || requestedTab === 'references') { + setActiveTab(requestedTab); + } + + if (!sku) return; + + const existingProduct = catalog.products.find(product => product.sku.toLowerCase() === sku.toLowerCase()); + setActiveTab('products'); + setProductFilter('all'); + setStatus('idle'); + + if (existingProduct) { + setProductForm({ + type: existingProduct.type, + sku: existingProduct.sku, + name: existingProduct.name, + categoryId: existingProduct.categoryId ? String(existingProduct.categoryId) : '', + composition: existingProduct.composition || '', + notes: existingProduct.notes || '', + gramature: existingProduct.gramature ? String(existingProduct.gramature) : '', + materialYield: existingProduct.materialYield ? String(existingProduct.materialYield) : '', + widthCm: existingProduct.widthCm ? String(existingProduct.widthCm) : '', + color: existingProduct.color || searchParams.get('color') || '', + subcategory: existingProduct.subcategory || 'malha', + sizes: existingProduct.sizes?.length ? existingProduct.sizes : defaultProductForm.sizes + }); + setFeedback(`Editando SKU ${existingProduct.sku}.`); + return; + } + + const requestedSize = (searchParams.get('size') || '').trim().toUpperCase(); + setProductForm({ + ...defaultProductForm, + sku, + name: searchParams.get('name') || '', + color: searchParams.get('color') || '', + sizes: requestedSize ? [requestedSize] : defaultProductForm.sizes + }); + setFeedback(`Novo cadastro para SKU ${sku}.`); + }); + }, [catalog.products, searchParams]); + const finishedProducts = useMemo( () => catalog.products.filter(product => product.type === 'finished_product'), [catalog.products] @@ -464,21 +521,32 @@ const Registrations = () => { ) : ( -
-

Tamanhos disponiveis

-
- {sizeOptions.map(size => ( - - ))} + <> + +
+

Tamanhos disponiveis

+
+ {sizeOptions.map(size => ( + + ))} +
-
+ )}