Add pagination to transaction preview for large imports

When uploading files with more than 100 transactions, the preview now displays transactions in pages of 100 rows at a time. This prevents browser freezing when rendering thousands of form inputs and dramatically improves page load performance.

- Show first 100 transactions by default with pagination controls
- All transactions remain in DOM but hidden for instant page switching
- Update counters and form submission to work across all pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
AJ
2025-10-11 22:17:46 -04:00
parent 3d6b47d537
commit c44929afe1

View File

@@ -44,6 +44,14 @@
<div class="card shadow-sm mb-3"> <div class="card shadow-sm mb-3">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<strong>Transaction Preview (<span id="selectedCount">@Model.PreviewTransactions.Count(p => !p.IsDuplicate)</span> selected, @Model.PreviewTransactions.Count(p => p.IsDuplicate) duplicates)</strong> <strong>Transaction Preview (<span id="selectedCount">@Model.PreviewTransactions.Count(p => !p.IsDuplicate)</span> selected, @Model.PreviewTransactions.Count(p => p.IsDuplicate) duplicates)</strong>
@if (Model.PreviewTransactions.Count > 100)
{
<div class="d-flex align-items-center gap-2">
<small class="text-muted">Showing <span id="pageStart">1</span>-<span id="pageEnd">100</span> of @Model.PreviewTransactions.Count</small>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="previousPage()">Previous</button>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="nextPage()">Next</button>
</div>
}
</div> </div>
<div class="card-body p-0"> <div class="card-body p-0">
<div class="table-responsive" style="max-height: 500px; overflow-y: auto;"> <div class="table-responsive" style="max-height: 500px; overflow-y: auto;">
@@ -62,11 +70,12 @@
<th style="width: 80px;">Status</th> <th style="width: 80px;">Status</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="transactionTableBody">
@for (int i = 0; i < Model.PreviewTransactions.Count; i++) @for (int i = 0; i < Model.PreviewTransactions.Count; i++)
{ {
var preview = Model.PreviewTransactions[i]; var preview = Model.PreviewTransactions[i];
<tr class="@(preview.IsDuplicate ? "table-secondary text-muted" : "")" data-index="@i"> var displayStyle = i >= 100 ? "display: none;" : "";
<tr class="@(preview.IsDuplicate ? "table-secondary text-muted" : "") transaction-row" data-index="@i" style="@displayStyle">
<td> <td>
<input type="checkbox" data-index="@i" <input type="checkbox" data-index="@i"
class="form-check-input transaction-checkbox" class="form-check-input transaction-checkbox"
@@ -227,6 +236,10 @@ else
@section Scripts { @section Scripts {
<partial name="_ValidationScriptsPartial" /> <partial name="_ValidationScriptsPartial" />
<script> <script>
let currentPage = 0;
const pageSize = 100;
const totalRows = @Model.PreviewTransactions.Count;
function togglePaymentSelection() { function togglePaymentSelection() {
const mode = document.querySelector('input[name="PaymentMode"]:checked').value; const mode = document.querySelector('input[name="PaymentMode"]:checked').value;
const cardRow = document.getElementById('cardSelectRow'); const cardRow = document.getElementById('cardSelectRow');
@@ -246,8 +259,10 @@ else
function updateSelectedCount() { function updateSelectedCount() {
const checkboxes = document.querySelectorAll('.transaction-checkbox:checked'); const checkboxes = document.querySelectorAll('.transaction-checkbox:checked');
const count = checkboxes.length; const count = checkboxes.length;
document.getElementById('selectedCount').textContent = count; const selectedCountEl = document.getElementById('selectedCount');
document.getElementById('selectedCountButton').textContent = count; const selectedCountButtonEl = document.getElementById('selectedCountButton');
if (selectedCountEl) selectedCountEl.textContent = count;
if (selectedCountButtonEl) selectedCountButtonEl.textContent = count;
} }
function updateAllTransactionAccounts() { function updateAllTransactionAccounts() {
@@ -255,6 +270,39 @@ else
// No action needed - we'll read the global account on form submit // No action needed - we'll read the global account on form submit
} }
function showPage(pageNum) {
const rows = document.querySelectorAll('.transaction-row');
const start = pageNum * pageSize;
const end = start + pageSize;
rows.forEach((row, index) => {
row.style.display = (index >= start && index < end) ? '' : 'none';
});
// Update pagination display
const pageStartEl = document.getElementById('pageStart');
const pageEndEl = document.getElementById('pageEnd');
if (pageStartEl && pageEndEl) {
pageStartEl.textContent = start + 1;
pageEndEl.textContent = Math.min(end, totalRows);
}
currentPage = pageNum;
}
function nextPage() {
const maxPage = Math.ceil(totalRows / pageSize) - 1;
if (currentPage < maxPage) {
showPage(currentPage + 1);
}
}
function previousPage() {
if (currentPage > 0) {
showPage(currentPage - 1);
}
}
function prepareFormData() { function prepareFormData() {
// Collect selected indices // Collect selected indices
const selectedIndices = []; const selectedIndices = [];
@@ -264,7 +312,7 @@ else
document.getElementById('selectedIndices').value = selectedIndices.join(','); document.getElementById('selectedIndices').value = selectedIndices.join(',');
// Get the global account ID // Get the global account ID
const globalAccountId = document.getElementById('globalAccountId').value; const globalAccountId = document.getElementById('globalAccountId')?.value;
if (!globalAccountId) { if (!globalAccountId) {
alert('Please select an account for all transactions'); alert('Please select an account for all transactions');
return false; return false;
@@ -292,6 +340,11 @@ else
} }
// Initialize on page load // Initialize on page load
document.addEventListener('DOMContentLoaded', togglePaymentSelection); document.addEventListener('DOMContentLoaded', function() {
togglePaymentSelection();
if (totalRows > pageSize) {
showPage(0);
}
});
</script> </script>
} }