From d6d7df12fd71466ce392e2b897d7839e47b0f853 Mon Sep 17 00:00:00 2001 From: "Ang Wei Feng (Ted)" Date: Sat, 11 Apr 2026 05:06:32 +0800 Subject: [PATCH] fix(accounts): add duplicate action to activity view (#1418) --- .../controllers/bulk_select_controller.js | 17 +++++++- app/views/entries/_selection_bar.html.erb | 11 +++++ app/views/trades/_trade.html.erb | 1 + app/views/transactions/_transaction.html.erb | 1 + app/views/valuations/_valuation.html.erb | 2 +- test/system/account_activity_test.rb | 43 +++++++++++++++++++ 6 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 test/system/account_activity_test.rb diff --git a/app/javascript/controllers/bulk_select_controller.js b/app/javascript/controllers/bulk_select_controller.js index 271b6a0f5..97b024a82 100644 --- a/app/javascript/controllers/bulk_select_controller.js +++ b/app/javascript/controllers/bulk_select_controller.js @@ -138,8 +138,13 @@ export default class extends Controller { count > 0; if (this.hasDuplicateLinkTarget) { - this.duplicateLinkTarget.classList.toggle("hidden", count !== 1); - if (count === 1) { + const selectedRow = this._selectedRow(); + const canDuplicate = + count === 1 && selectedRow?.dataset.entryType === "Transaction"; + + this.duplicateLinkTarget.classList.toggle("hidden", !canDuplicate); + + if (canDuplicate) { const url = new URL( this.duplicateLinkTarget.href, window.location.origin, @@ -158,6 +163,14 @@ export default class extends Controller { return this.pluralLabelValue; } + _selectedRow() { + if (this.selectedIdsValue.length !== 1) return null; + + return this.rowTargets.find( + (row) => row.dataset.id === this.selectedIdsValue[0], + ); + } + _updateGroups() { this.groupTargets.forEach((group) => { const rows = this.rowTargets.filter( diff --git a/app/views/entries/_selection_bar.html.erb b/app/views/entries/_selection_bar.html.erb index 5bd4b5788..19033d78e 100644 --- a/app/views/entries/_selection_bar.html.erb +++ b/app/views/entries/_selection_bar.html.erb @@ -7,6 +7,17 @@
<%= turbo_frame_tag "bulk_transaction_edit_drawer" %> + + <%= link_to new_transaction_path, + class: "p-1.5 group/duplicate hover:bg-inverse flex items-center justify-center rounded-md hidden", + title: t("transactions.selection_bar.duplicate"), + data: { + turbo_frame: "modal", + bulk_select_target: "duplicateLink" + } do %> + <%= icon "copy", class: "group-hover/duplicate:text-inverse" %> + <% end %> + <%= link_to new_transactions_bulk_update_path, class: "p-1.5 group/edit hover:bg-inverse flex items-center justify-center rounded-md", title: "Edit", diff --git a/app/views/trades/_trade.html.erb b/app/views/trades/_trade.html.erb index b14a7527c..01f690c58 100644 --- a/app/views/trades/_trade.html.erb +++ b/app/views/trades/_trade.html.erb @@ -10,6 +10,7 @@ class: "checkbox checkbox--light hidden lg:block", data: { id: entry.id, + entry_type: entry.entryable_type, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection", checkbox_toggle_target: "selectionEntry" diff --git a/app/views/transactions/_transaction.html.erb b/app/views/transactions/_transaction.html.erb index 8366388cb..0fa1a608c 100644 --- a/app/views/transactions/_transaction.html.erb +++ b/app/views/transactions/_transaction.html.erb @@ -12,6 +12,7 @@ class: "checkbox checkbox--light hidden lg:block", data: { id: entry.id, + entry_type: entry.entryable_type, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection", checkbox_toggle_target: "selectionEntry" diff --git a/app/views/valuations/_valuation.html.erb b/app/views/valuations/_valuation.html.erb index 46ca62eda..e377eda0f 100644 --- a/app/views/valuations/_valuation.html.erb +++ b/app/views/valuations/_valuation.html.erb @@ -11,7 +11,7 @@
<%= check_box_tag dom_id(entry, "selection"), class: "checkbox checkbox--light hidden lg:block", - data: { id: entry.id, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection", checkbox_toggle_target: "selectionEntry" } %> + data: { id: entry.id, entry_type: entry.entryable_type, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection", checkbox_toggle_target: "selectionEntry" } %>
<%= render DS::FilledIcon.new(icon: icon, size: "md", hex_color: color, rounded: true) %> diff --git a/test/system/account_activity_test.rb b/test/system/account_activity_test.rb new file mode 100644 index 000000000..5349b8b10 --- /dev/null +++ b/test/system/account_activity_test.rb @@ -0,0 +1,43 @@ +require "application_system_test_case" + +class AccountActivityTest < ApplicationSystemTestCase + setup do + sign_in users(:family_admin) + + @account = accounts(:depository) + @transaction_entry = @account.entries.create!( + name: "Duplicate source", + date: Date.current, + amount: 42.50, + currency: "USD", + entryable: Transaction.new + ) + @valuation_entry = @account.entries.create!( + name: "Current balance", + date: 1.day.ago.to_date, + amount: 1000, + currency: "USD", + entryable: Valuation.new + ) + end + + test "account activity shows duplicate action for a selected transaction" do + visit account_url(@account, tab: "activity") + + find("#" + dom_id(@transaction_entry, "selection")).check + + within "#entry-selection-bar" do + assert_selector "a[title='Duplicate']:not(.hidden)" + end + end + + test "account activity hides duplicate action for a selected valuation" do + visit account_url(@account, tab: "activity") + + find("#" + dom_id(@valuation_entry, "selection")).check + + within "#entry-selection-bar" do + assert_selector "a[title='Duplicate'].hidden", visible: false + end + end +end