diff --git a/src/pages/Cutting.tsx b/src/pages/Cutting.tsx index 6080651..6cc3ec9 100644 --- a/src/pages/Cutting.tsx +++ b/src/pages/Cutting.tsx @@ -12,6 +12,7 @@ import type { CuttingSettings, DateRange, ProductAnalyticsItem, ProductCompositi import { getPlanningStock } from '../planningStock'; type CutFilter = 'need' | 'all' | 'issues' | 'covered'; +type ExecutionFilter = 'all' | 'ready' | 'blocked' | 'data'; type CutSort = 'need_desc' | 'need_asc' | 'demand_desc' | 'stock_asc' | 'sold_desc' | 'name_asc'; type SaveStatus = 'idle' | 'saving' | 'saved' | 'error'; type CutProductIssue = Exclude; @@ -146,6 +147,7 @@ const Cutting = () => { const [targetCoverageDays, setTargetCoverageDays] = useState(30); const [familyFilter, setFamilyFilter] = useState('all'); const [cutFilter, setCutFilter] = useState('need'); + const [executionFilter, setExecutionFilter] = useState('all'); const [sortBy, setSortBy] = useState('need_desc'); const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [settingsSection, setSettingsSection] = useState('rules'); @@ -309,7 +311,15 @@ const Cutting = () => { return row.suggestedCutQuantity === 0; }); - return [...statusRows].sort((a, b) => { + const executionRows = statusRows.filter(row => { + if (executionFilter === 'all') return true; + const status = materialReadinessByProductId.get(row.id)?.status; + if (executionFilter === 'ready') return row.suggestedCutQuantity > 0 && status === 'ready'; + if (executionFilter === 'blocked') return row.suggestedCutQuantity > 0 && (status === 'blocked' || status === 'missing'); + return row.suggestedCutQuantity > 0 && status === 'material_ready'; + }); + + return [...executionRows].sort((a, b) => { switch (sortBy) { case 'need_asc': return a.suggestedCutQuantity - b.suggestedCutQuantity; case 'demand_desc': return b.projectedDemand - a.projectedDemand; @@ -321,7 +331,7 @@ const Cutting = () => { return b.suggestedCutQuantity - a.suggestedCutQuantity; } }); - }, [cutFilter, cutPlan.rows, familyFilter, searchTerm, sortBy]); + }, [cutFilter, cutPlan.rows, executionFilter, familyFilter, materialReadinessByProductId, searchTerm, sortBy]); const rowsAvailableForOrder = useMemo(() => ( filteredRows.filter(row => row.suggestedCutQuantity > 0 && materialReadinessByProductId.get(row.id)?.status === 'ready') @@ -599,12 +609,18 @@ const Cutting = () => { ); }; - const renderMaterialReadiness = (row: CutPlanSkuRow) => { + const renderCutStatus = (row: CutPlanSkuRow) => { const readiness = materialReadinessByProductId.get(row.id); - if (readiness?.status === 'ready') return Pode cortar; - if (readiness?.status === 'material_ready') return Material OK · revisar dados; - if (readiness?.status === 'missing') return Sem composição; - return Falta: {readiness?.blockers[0] || 'material'}; + if (readiness?.status === 'ready') { + return Pronto para cortar; + } + if (readiness?.status === 'material_ready') { + return issueLabels[issue]).join(', ')}>Dados pendentes; + } + if (readiness?.status === 'missing') { + return Sem composição; + } + return Falta: {readiness?.blockers[0] || 'material'}; }; return ( @@ -1241,6 +1257,29 @@ const Cutting = () => { {activeCuttingView === 'plan' && ( <> +
+ {[ + { id: 'all' as const, label: 'Fila de corte', detail: `${formatNumber(cutPlan.summary.skuCount)} SKUs com necessidade`, value: cutExecutionSummary.rawUnits, tone: 'default' }, + { id: 'ready' as const, label: 'Pronto para cortar', detail: `${formatNumber(cutExecutionSummary.readyRows)} SKUs liberados`, value: cutExecutionSummary.readyUnits, tone: 'ready' }, + { id: 'blocked' as const, label: 'Falta material', detail: `${formatNumber(cutExecutionSummary.blockedByMaterial)} SKUs bloqueados`, value: cutExecutionSummary.blockedByMaterial, tone: 'blocked' }, + { id: 'data' as const, label: 'Dados pendentes', detail: 'Material disponível, cadastro incompleto', value: cutExecutionSummary.blockedByData, tone: 'data' }, + ].map(option => ( + + ))} +
@@ -1313,63 +1352,37 @@ const Cutting = () => { ) : (
- +
- - - - - - + + + - - - + - - - - + - - - + {paginatedRows.map(row => ( - - - - - - +
ID ProdutoDescriçãoFamíliaVariaçãoProduto Demanda Estoque NecessidadeRolosMateriais - Dados pendentes - Situação para corte Ações
#{row.id}
{row.name}
-
- Média: {formatNumber(row.dailySales, 2)} un./dia · Cobertura: {formatDays(row.daysOfCover)} -
-
- - {row.family.materialLabel} - - -
- - - - {row.size || '-'} - +
+ #{row.id} + {row.family.materialLabel} + + {row.size || '-'}
+
Média: {formatNumber(row.dailySales, 2)} un./dia · Cobertura: {formatDays(row.daysOfCover)}
{formatNumber(row.projectedDemand, 1)} un. @@ -1383,11 +1396,7 @@ const Cutting = () => { {formatNumber(row.suggestedCutQuantity)} un. - {row.estimatedRolls === null ? '-' : formatNumber(row.estimatedRolls)} - {renderMaterialReadiness(row)}{renderIssueBadge(row)}{renderCutStatus(row)}