Add pagination to Pricing tab

Load data in batches of 100 products per page with navigation controls
This commit is contained in:
Christian Vidal Wolf
2026-04-21 11:25:20 +02:00
parent 99ad4f7d25
commit 9d169a0bdd
+51 -4
View File
@@ -51,6 +51,8 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
const [filterMode, setFilterMode] = useState<FilterMode>('all_errors');
const [search, setSearch] = useState('');
const [searchMode, setSearchMode] = useState<'all' | 'selected'>('all');
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 100;
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({
articleNo: 100,
articleName: 220,
@@ -387,6 +389,14 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
return sorted;
}, [filteredRows, sortConfig]);
// ── Pagination ─────────────────────────────────────────────────────────
const paginatedRows = useMemo(() => {
const start = (currentPage - 1) * pageSize;
return sortedRows.slice(start, start + pageSize);
}, [sortedRows, currentPage]);
const totalPages = Math.ceil(sortedRows.length / pageSize);
const handleSort = (key: string | number) => {
setSortConfig(prev => {
if (prev.key === key) {
@@ -713,7 +723,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
{FILTERS.map(f => (
<button
key={f.id}
onClick={() => setFilterMode(f.id)}
onClick={() => { setFilterMode(f.id); setCurrentPage(1); }}
className={cn(
'flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
filterMode === f.id
@@ -1056,7 +1066,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
</tr>
</thead>
<tbody>
{sortedRows.map(({ row, dataIndex, pricingErrors, unitErrors, isCritical }) => {
{paginatedRows.map(({ row, dataIndex, pricingErrors, unitErrors, isCritical }) => {
const saveStatus = rowStatuses[String(row[COLUMNS.ARTICLE_NO])];
const isValidated = !!row[COLUMNS.VALIDATED_CHECK];
const note = String(row[COLUMNS.VALIDATED_NOTE] || '');
@@ -1274,13 +1284,50 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
)}
</div>
{/* ── Footer count ── */}
{/* ── Footer count & Pagination ── */}
<div className="flex items-center justify-between">
<p className="text-xs text-slate-500 pb-1">
Showing {filteredRows.length} of {stats.total} products
Showing {paginatedRows.length} of {sortedRows.length} products {totalPages > 1 && `(${currentPage}/${totalPages})`}
{pricingEditableCols.length > 0 && (
<> · Click any <span className="text-blue-400">price cell</span> to edit inline</>
)}
</p>
{totalPages > 1 && (
<div className="flex items-center gap-1">
<button
onClick={() => setCurrentPage(1)}
disabled={currentPage === 1}
className="px-2 py-1 text-xs bg-slate-800 hover:bg-slate-700 rounded disabled:opacity-50 disabled:cursor-not-allowed"
>
««
</button>
<button
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
disabled={currentPage === 1}
className="px-2 py-1 text-xs bg-slate-800 hover:bg-slate-700 rounded disabled:opacity-50 disabled:cursor-not-allowed"
>
«
</button>
<span className="text-xs text-slate-400 px-2">
{currentPage} / {totalPages}
</span>
<button
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
disabled={currentPage === totalPages}
className="px-2 py-1 text-xs bg-slate-800 hover:bg-slate-700 rounded disabled:opacity-50 disabled:cursor-not-allowed"
>
»
</button>
<button
onClick={() => setCurrentPage(totalPages)}
disabled={currentPage === totalPages}
className="px-2 py-1 text-xs bg-slate-800 hover:bg-slate-700 rounded disabled:opacity-50 disabled:cursor-not-allowed"
>
»»
</button>
</div>
)}
</div>
</div>
);
}