diff --git a/src/pages/Cutting.tsx b/src/pages/Cutting.tsx index ea02bd8..3ae4e93 100644 --- a/src/pages/Cutting.tsx +++ b/src/pages/Cutting.tsx @@ -11,6 +11,8 @@ import type { CuttingSettings, DateRange, ProductAnalyticsItem, ProductionOrderI type CutFilter = 'need' | 'all' | 'issues' | 'covered'; type CutSort = 'need_desc' | 'need_asc' | 'demand_desc' | 'stock_asc' | 'sold_desc' | 'name_asc'; type SaveStatus = 'idle' | 'saving' | 'saved' | 'error'; +type CutProductIssue = Exclude; +type CorrectionIssueFilter = CutProductIssue | 'all'; const SETTINGS_STORAGE_KEY = 'nexstar_cutting_settings'; const coverageTargetOptions = [7, 15, 30, 60]; @@ -41,6 +43,13 @@ const issueHelp: Record = { missing_yield_rule: 'Família reconhecida, mas ainda falta cadastrar unidades por rolo.' }; +const correctionFilterOptions: Array<{ value: CorrectionIssueFilter; label: string }> = [ + { value: 'all', label: 'Todas pendências' }, + { value: 'missing_family_rule', label: issueLabels.missing_family_rule }, + { value: 'missing_color', label: issueLabels.missing_color }, + { value: 'missing_size', label: issueLabels.missing_size } +]; + const familyStyles: Record = { BLCS: 'border-emerald-400/30 bg-emerald-400/10 text-emerald-300', BLOS: 'border-sky-400/30 bg-sky-400/10 text-sky-300', @@ -117,6 +126,8 @@ const Cutting = () => { const [cuttingSettings, setCuttingSettings] = useState(loadCuttingSettings); const [saveStatus, setSaveStatus] = useState('idle'); const [hasUnsavedSettings, setHasUnsavedSettings] = useState(false); + const [correctionIssueFilter, setCorrectionIssueFilter] = useState('all'); + const [correctionPage, setCorrectionPage] = useState(1); const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); @@ -228,12 +239,31 @@ const Cutting = () => { const startIndex = (safeCurrentPage - 1) * itemsPerPage; const paginatedRows = filteredRows.slice(startIndex, startIndex + itemsPerPage); const isRefreshing = isLoading && products.length > 0; - const correctionRows = cutPlan.rows.filter(row => ( - row.issues.some(issue => issue !== 'missing_yield_rule') - )).slice(0, 12); + const correctionRows = useMemo(() => { + const rows = cutPlan.rows.filter(row => ( + row.issues.some(issue => issue !== 'missing_yield_rule') && + (correctionIssueFilter === 'all' || row.issues.includes(correctionIssueFilter)) + )); + + return [...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]); + const correctionItemsPerPage = 12; + const correctionTotalPages = Math.ceil(correctionRows.length / correctionItemsPerPage); + const safeCorrectionPage = Math.min(correctionPage, correctionTotalPages || 1); + const correctionStartIndex = (safeCorrectionPage - 1) * correctionItemsPerPage; + const paginatedCorrectionRows = correctionRows.slice(correctionStartIndex, correctionStartIndex + correctionItemsPerPage); const configuredYieldCount = CUT_FAMILY_RULES.filter(rule => cuttingSettings.familyYields[rule.key]).length; const productOverrideCount = Object.keys(cuttingSettings.productOverrides).length; + useEffect(() => { + setCorrectionPage(1); + }, [correctionIssueFilter, dateRange, targetCoverageDays]); + const updateFamilyYield = (familyKey: CutFamilyKey, value: string) => { const parsedValue = Number(value); setCuttingSettings(current => { @@ -436,68 +466,124 @@ const Cutting = () => { ))} - {!!correctionRows.length && ( -
-
- ID - Produto - Família - Cor - Tamanho - Ações +
+
+
+

Correções de produto

+

+ Use quando o nome do SKU não deixa claro família, cor ou tamanho para montar o corte. +

-
- {correctionRows.map(row => { - const override = cuttingSettings.productOverrides[row.id] || {}; - return ( -
- #{row.id} -
-
{row.name}
-
- {row.issues.map(issue => issueLabels[issue]).join(', ')} -
-
- - updateProductOverride(row.id, { color: event.target.value })} - placeholder={row.color || 'Cor'} - className="h-9 rounded-lg border border-dark-border bg-dark-input px-2 text-xs font-bold text-dark-text outline-none focus:border-brand-primary" - /> - updateProductOverride(row.id, { size: event.target.value })} - placeholder={row.size || 'Tam.'} - className="h-9 rounded-lg border border-dark-border bg-dark-input px-2 text-xs font-bold text-dark-text outline-none focus:border-brand-primary" - /> -
- -
-
- ); - })} +
+ + {formatNumber(correctionRows.length)} produtos + +
- )} + + {correctionRows.length ? ( + <> +
+
+
+ ID + Produto + Família + Cor + Tamanho + Ações +
+
+ {paginatedCorrectionRows.map(row => { + const override = cuttingSettings.productOverrides[row.id] || {}; + return ( +
+ #{row.id} +
+
{row.name}
+
+ {row.issues.filter(issue => issue !== 'missing_yield_rule').map(issue => ( + + {issueLabels[issue]} + + ))} +
+
+ + updateProductOverride(row.id, { color: event.target.value })} + placeholder={row.color || 'Cor'} + className="h-9 rounded-lg border border-dark-border bg-dark-input px-2 text-xs font-bold text-dark-text outline-none focus:border-brand-primary" + /> + updateProductOverride(row.id, { size: event.target.value })} + placeholder={row.size || 'Tam.'} + className="h-9 rounded-lg border border-dark-border bg-dark-input px-2 text-xs font-bold text-dark-text outline-none focus:border-brand-primary" + /> +
+ +
+
+ ); + })} +
+
+
+ + setCorrectionPage(1)} + className="border-t border-dark-border px-4 py-3" + /> + + ) : ( +
+

Nenhuma correção de produto pendente.

+

As pendências restantes, se existirem, são de rendimento por família.

+
+ )} +
)} @@ -579,7 +665,12 @@ const Cutting = () => { type="button" onClick={() => { setCutFilter('issues'); + setIsSettingsOpen(true); + if (summary.issue !== 'missing_yield_rule') { + setCorrectionIssueFilter(summary.issue); + } setCurrentPage(1); + setCorrectionPage(1); }} className="rounded-xl border border-dark-border bg-dark-card p-3 text-left transition-colors hover:border-amber-400/50 cursor-pointer" >