Paginate production order preview
This commit is contained in:
@@ -159,6 +159,8 @@ const Cutting = () => {
|
|||||||
const [itemsPerPage, setItemsPerPage] = useState(10);
|
const [itemsPerPage, setItemsPerPage] = useState(10);
|
||||||
const [isGeneratingOrders, setIsGeneratingOrders] = useState(false);
|
const [isGeneratingOrders, setIsGeneratingOrders] = useState(false);
|
||||||
const [isOrderPreviewOpen, setIsOrderPreviewOpen] = useState(false);
|
const [isOrderPreviewOpen, setIsOrderPreviewOpen] = useState(false);
|
||||||
|
const [previewPage, setPreviewPage] = useState(1);
|
||||||
|
const [previewItemsPerPage, setPreviewItemsPerPage] = useState(10);
|
||||||
const [editingCutRow, setEditingCutRow] = useState<CutPlanSkuRow | null>(null);
|
const [editingCutRow, setEditingCutRow] = useState<CutPlanSkuRow | null>(null);
|
||||||
const [quickCutEdit, setQuickCutEdit] = useState<CutProductOverride>({});
|
const [quickCutEdit, setQuickCutEdit] = useState<CutProductOverride>({});
|
||||||
const [isQuickCutEditSaving, setIsQuickCutEditSaving] = useState(false);
|
const [isQuickCutEditSaving, setIsQuickCutEditSaving] = useState(false);
|
||||||
@@ -364,6 +366,14 @@ const Cutting = () => {
|
|||||||
rowsAvailableForOrder.reduce((total, row) => total + row.suggestedCutQuantity, 0)
|
rowsAvailableForOrder.reduce((total, row) => total + row.suggestedCutQuantity, 0)
|
||||||
), [rowsAvailableForOrder]);
|
), [rowsAvailableForOrder]);
|
||||||
|
|
||||||
|
const previewRowsWithIssues = useMemo(() => (
|
||||||
|
rowsAvailableForOrder.filter(row => row.issues.length).length
|
||||||
|
), [rowsAvailableForOrder]);
|
||||||
|
const previewTotalPages = Math.ceil(rowsAvailableForOrder.length / previewItemsPerPage);
|
||||||
|
const safePreviewPage = Math.min(previewPage, previewTotalPages || 1);
|
||||||
|
const previewStartIndex = (safePreviewPage - 1) * previewItemsPerPage;
|
||||||
|
const paginatedPreviewRows = rowsAvailableForOrder.slice(previewStartIndex, previewStartIndex + previewItemsPerPage);
|
||||||
|
|
||||||
const totalPages = Math.ceil(filteredRows.length / itemsPerPage);
|
const totalPages = Math.ceil(filteredRows.length / itemsPerPage);
|
||||||
const safeCurrentPage = Math.min(currentPage, totalPages || 1);
|
const safeCurrentPage = Math.min(currentPage, totalPages || 1);
|
||||||
const startIndex = (safeCurrentPage - 1) * itemsPerPage;
|
const startIndex = (safeCurrentPage - 1) * itemsPerPage;
|
||||||
@@ -587,6 +597,7 @@ const Cutting = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setGenerationMessage('');
|
setGenerationMessage('');
|
||||||
|
setPreviewPage(1);
|
||||||
setIsOrderPreviewOpen(true);
|
setIsOrderPreviewOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -777,60 +788,51 @@ const Cutting = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-3 border-b border-dark-border p-4 md:grid-cols-3">
|
<div className="grid grid-cols-2 divide-x divide-y divide-dark-border border-b border-dark-border md:grid-cols-4 md:divide-y-0">
|
||||||
<div className="rounded-xl border border-dark-border bg-dark-input/60 p-3">
|
{[
|
||||||
<p className="text-[10px] font-bold uppercase tracking-widest text-dark-muted">OPs previstas</p>
|
['OPs a criar', formatNumber(rowsAvailableForOrder.length), 'text-dark-text'],
|
||||||
<p className="mt-2 text-2xl font-bold text-dark-text">{formatNumber(rowsAvailableForOrder.length)}</p>
|
['Unidades para cortar', `${formatNumber(orderPreviewTotalUnits)} un.`, 'text-brand-primary'],
|
||||||
</div>
|
['Cobertura alvo', `${formatNumber(targetCoverageDays)} dias`, 'text-dark-text'],
|
||||||
<div className="rounded-xl border border-dark-border bg-dark-input/60 p-3">
|
['Revisar dados', formatNumber(previewRowsWithIssues), previewRowsWithIssues ? 'text-amber-300' : 'text-dark-text']
|
||||||
<p className="text-[10px] font-bold uppercase tracking-widest text-dark-muted">Unidades totais</p>
|
].map(([label, value, tone]) => <div key={label} className="p-4"><p className="text-[10px] font-bold uppercase tracking-widest text-dark-muted">{label}</p><p className={`mt-1 text-xl font-bold ${tone}`}>{value}</p></div>)}
|
||||||
<p className="mt-2 text-2xl font-bold text-red-300">{formatNumber(orderPreviewTotalUnits)} un.</p>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-xl border border-dark-border bg-dark-input/60 p-3">
|
|
||||||
<p className="text-[10px] font-bold uppercase tracking-widest text-dark-muted">Cobertura alvo</p>
|
|
||||||
<p className="mt-2 text-2xl font-bold text-brand-primary">{formatNumber(targetCoverageDays)} dias</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="overflow-auto">
|
<div className="overflow-auto">
|
||||||
<table className="w-full min-w-[980px] text-left text-sm">
|
<table className="w-full min-w-[900px] table-fixed text-left text-sm">
|
||||||
|
<colgroup>
|
||||||
|
<col className="w-[44%]" />
|
||||||
|
<col className="w-[16%]" />
|
||||||
|
<col className="w-[14%]" />
|
||||||
|
<col className="w-[14%]" />
|
||||||
|
<col className="w-[12%]" />
|
||||||
|
</colgroup>
|
||||||
<thead className="border-b border-dark-border bg-dark-header text-dark-muted">
|
<thead className="border-b border-dark-border bg-dark-header text-dark-muted">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">SKU</th>
|
|
||||||
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Produto</th>
|
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Produto</th>
|
||||||
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Família</th>
|
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Cortar agora</th>
|
||||||
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Variação</th>
|
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Demanda</th>
|
||||||
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Necessidade</th>
|
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Disponível</th>
|
||||||
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Dados</th>
|
<th className="px-4 py-3 text-[10px] font-bold uppercase tracking-wider">Revisar</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-dark-border">
|
<tbody className="divide-y divide-dark-border">
|
||||||
{rowsAvailableForOrder.map(row => (
|
{paginatedPreviewRows.map(row => (
|
||||||
<tr key={row.id} className="hover:bg-dark-input/50">
|
<tr key={row.id} className="hover:bg-dark-input/50">
|
||||||
<td className="px-4 py-3 font-mono text-[11px] text-dark-muted">#{row.id}</td>
|
|
||||||
<td className="max-w-0 px-4 py-3">
|
<td className="max-w-0 px-4 py-3">
|
||||||
<div className="truncate font-bold text-dark-text" title={row.name}>{row.name}</div>
|
<div className="truncate font-bold text-dark-text" title={row.name}>{row.name}</div>
|
||||||
<div className="mt-1 text-[10px] font-semibold text-dark-muted">
|
<div className="mt-1 flex flex-wrap items-center gap-1.5">
|
||||||
Média: {formatNumber(row.dailySales, 2)} un./dia · Estoque: {formatNumber(row.stock)} un.
|
<span className="font-mono text-[10px] font-medium text-dark-muted">#{row.id}</span>
|
||||||
</div>
|
<span className={`inline-flex whitespace-nowrap rounded-full border px-2 py-0.5 text-[10px] font-bold ${familyStyles[row.family.key]}`}>{row.family.materialLabel}</span>
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<span className={`inline-flex whitespace-nowrap rounded-full border px-2.5 py-1 text-xs font-bold ${familyStyles[row.family.key]}`}>
|
|
||||||
{row.family.materialLabel}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div className="flex min-w-0 items-center gap-1.5">
|
|
||||||
<ProductColorBadge label={row.color} className="max-w-[110px] px-2" />
|
<ProductColorBadge label={row.color} className="max-w-[110px] px-2" />
|
||||||
<span className="inline-flex items-center gap-1 rounded-full border border-emerald-400/25 bg-emerald-400/10 px-2 py-1 text-xs font-bold text-emerald-300">
|
<span className="inline-flex items-center gap-1 rounded-full border border-emerald-400/25 bg-emerald-400/10 px-2 py-0.5 text-[10px] font-bold text-emerald-300">
|
||||||
<Ruler className="h-3 w-3" />
|
<Ruler className="h-3 w-3" />
|
||||||
{row.size || '-'}
|
{row.size || '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 font-bold whitespace-nowrap text-red-300">
|
<td className="px-4 py-3 font-bold whitespace-nowrap text-brand-primary">{formatNumber(row.suggestedCutQuantity)} un.</td>
|
||||||
{formatNumber(row.suggestedCutQuantity)} un.
|
<td className="px-4 py-3 font-bold whitespace-nowrap text-dark-text">{formatNumber(row.projectedDemand, 1)} un.</td>
|
||||||
</td>
|
<td className="px-4 py-3 whitespace-nowrap"><div className="font-bold text-dark-text">{formatNumber(row.availableQuantity)} un.</div>{row.openProductionQuantity ? <div className="mt-1 text-[10px] font-semibold text-dark-muted">{formatNumber(row.openProductionQuantity)} em OP</div> : null}</td>
|
||||||
<td className="px-4 py-3">{renderIssueBadge(row)}</td>
|
<td className="px-4 py-3">{renderIssueBadge(row)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
@@ -838,6 +840,21 @@ const Cutting = () => {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<PaginationControls
|
||||||
|
totalItems={rowsAvailableForOrder.length}
|
||||||
|
currentPage={safePreviewPage}
|
||||||
|
totalPages={previewTotalPages}
|
||||||
|
pageSize={previewItemsPerPage}
|
||||||
|
pageSizeOptions={[10, 20, 50]}
|
||||||
|
itemLabel="OPs"
|
||||||
|
pageSizeLabel="por página"
|
||||||
|
startIndex={previewStartIndex}
|
||||||
|
endIndex={Math.min(previewStartIndex + previewItemsPerPage, rowsAvailableForOrder.length)}
|
||||||
|
onPageChange={setPreviewPage}
|
||||||
|
onPageSizeChange={(pageSize) => { setPreviewItemsPerPage(pageSize); setPreviewPage(1); }}
|
||||||
|
className="border-t border-dark-border px-4 py-3"
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 border-t border-dark-border p-4 sm:flex-row sm:items-center sm:justify-between">
|
<div className="flex flex-col gap-3 border-t border-dark-border p-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<p className="text-xs font-semibold text-dark-muted">
|
<p className="text-xs font-semibold text-dark-muted">
|
||||||
OPs abertas já existentes para o mesmo plano e SKU serão ignoradas.
|
OPs abertas já existentes para o mesmo plano e SKU serão ignoradas.
|
||||||
|
|||||||
Reference in New Issue
Block a user