From f72c32c52f5ceca435dff8645c05da1cf8e26bae Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 15 Jan 2026 22:53:05 -0500 Subject: [PATCH] Feature: Add transaction multi-select for batch AI review Add selection UI to Transactions page: - Add checkbox column for selecting transactions - Add sticky selection bar showing selected count - Add Select All / Clear Selection controls - Wire up form to send selected IDs to AICategorizePreview page - Preserve row click navigation for unselected areas Co-Authored-By: Claude Opus 4.5 --- MoneyMap/Pages/Transactions.cshtml | 121 ++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 21 deletions(-) diff --git a/MoneyMap/Pages/Transactions.cshtml b/MoneyMap/Pages/Transactions.cshtml index 6771c06..59064c8 100644 --- a/MoneyMap/Pages/Transactions.cshtml +++ b/MoneyMap/Pages/Transactions.cshtml @@ -133,27 +133,53 @@ } + +
+
+
+ 0 transaction(s) selected +
+
+ + +
+ @Html.AntiForgeryToken() + +
+
+
+
+ @if (Model.Transactions.Any()) {
-
- @if (!string.IsNullOrWhiteSpace(Model.Category)) - { - @Model.Category - - @Model.Stats.Count transactions - } - else - { - All Transactions - - @Model.Stats.Count total - } +
+
+ @if (!string.IsNullOrWhiteSpace(Model.Category)) + { + @Model.Category + - @Model.Stats.Count transactions + } + else + { + All Transactions + - @Model.Stats.Count total + } +
+
+ + +
+ @@ -166,12 +192,16 @@ @foreach (var t in Model.Transactions) { - - - - + + + + - - + - - - } @@ -340,6 +369,56 @@ else }); } })(); + + // Transaction selection handling + function updateSelection() { + const checkboxes = document.querySelectorAll('.txn-checkbox:checked'); + const count = checkboxes.length; + const selectionBar = document.getElementById('selectionBar'); + const selectedCount = document.getElementById('selectedCount'); + const form = document.getElementById('aiReviewForm'); + + selectedCount.textContent = count; + + if (count > 0) { + selectionBar.classList.remove('d-none'); + // Update form with selected IDs + const existingInputs = form.querySelectorAll('input[name="transactionIds"]'); + existingInputs.forEach(input => input.remove()); + + checkboxes.forEach(cb => { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = 'transactionIds'; + input.value = cb.value; + form.appendChild(input); + }); + } else { + selectionBar.classList.add('d-none'); + } + + // Update select all checkbox state + const allCheckboxes = document.querySelectorAll('.txn-checkbox'); + const selectAllCheckbox = document.getElementById('selectAllCheckbox'); + selectAllCheckbox.checked = allCheckboxes.length > 0 && checkboxes.length === allCheckboxes.length; + selectAllCheckbox.indeterminate = checkboxes.length > 0 && checkboxes.length < allCheckboxes.length; + } + + function toggleSelectAll(checked) { + document.querySelectorAll('.txn-checkbox').forEach(cb => cb.checked = checked); + updateSelection(); + } + + function selectAllVisible() { + document.querySelectorAll('.txn-checkbox').forEach(cb => cb.checked = true); + updateSelection(); + } + + function clearSelection() { + document.querySelectorAll('.txn-checkbox').forEach(cb => cb.checked = false); + document.getElementById('selectAllCheckbox').checked = false; + updateSelection(); + } }
ID Date Name
#@t.Id@t.Date.ToString("yyyy-MM-dd") +
+ + #@t.Id@t.Date.ToString("yyyy-MM-dd")
- @t.Name + @t.Name @if (t.ReceiptCount > 0) { @@ -187,11 +217,11 @@ }
@t.Memo + @t.Memo @t.Amount.ToString("C") + @if (string.IsNullOrWhiteSpace(t.Category)) { (uncategorized) @@ -201,10 +231,9 @@ @t.Category } + @t.CardLabel