From 4a39f659877aa3e03ebafaf0ce7266062a9fa71b Mon Sep 17 00:00:00 2001 From: Alessio Cappa Date: Sat, 13 Dec 2025 13:43:21 +0100 Subject: [PATCH 01/25] feat: Add toggle on mobile to show/hide checkboxes in transaction page --- .../controllers/checkbox_toggle_controller.js | 32 +++++++++++++++++++ app/views/entries/_entry_group.html.erb | 7 ++-- app/views/transactions/_transaction.html.erb | 5 +-- app/views/transactions/index.html.erb | 2 +- .../transactions/searches/_form.html.erb | 10 ++++++ 5 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 app/javascript/controllers/checkbox_toggle_controller.js diff --git a/app/javascript/controllers/checkbox_toggle_controller.js b/app/javascript/controllers/checkbox_toggle_controller.js new file mode 100644 index 00000000000..e4f88dfc6e5 --- /dev/null +++ b/app/javascript/controllers/checkbox_toggle_controller.js @@ -0,0 +1,32 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["selectionEntry", "toggleButton"] + + toggle() { + const shouldShow = this.selectionEntryTargets[0].classList.contains("hidden") + + this.selectionEntryTargets.forEach((el) => { + if (shouldShow) { + el.classList.remove("hidden") + } else { + el.classList.add("hidden") + } + }) + + if (!shouldShow) { + const bulkSelectElement = this.element.closest("[data-controller~='bulk-select']"); + if (bulkSelectElement) { + const bulkSelectController = this.application.getControllerForElementAndIdentifier( + bulkSelectElement, + "bulk-select" + ); + if (bulkSelectController) { + bulkSelectController.deselectAll(); + } + } + } + + this.toggleButtonTarget.classList.toggle("bg-surface", shouldShow) + } +} diff --git a/app/views/entries/_entry_group.html.erb b/app/views/entries/_entry_group.html.erb index 2ee8954c12f..7a0d1447038 100644 --- a/app/views/entries/_entry_group.html.erb +++ b/app/views/entries/_entry_group.html.erb @@ -4,9 +4,12 @@
<%= check_box_tag "#{date}_entries_selection", - class: ["checkbox checkbox--light", "hidden": entries.size == 0], + class: ["checkbox checkbox--light hidden lg:block", "lg:hidden": entries.size == 0], id: "selection_entry_#{date}", - data: { action: "bulk-select#toggleGroupSelection" } %> + data: { + action: "bulk-select#toggleGroupSelection", + checkbox_toggle_target: "selectionEntry" + } %>

<%= tag.span I18n.l(date, format: :long) %> diff --git a/app/views/transactions/_transaction.html.erb b/app/views/transactions/_transaction.html.erb index 9b87c7aff10..86dd0e193bd 100644 --- a/app/views/transactions/_transaction.html.erb +++ b/app/views/transactions/_transaction.html.erb @@ -9,11 +9,12 @@

<%= check_box_tag dom_id(entry, "selection"), disabled: transaction.transfer.present?, - class: "checkbox checkbox--light", + class: "checkbox checkbox--light hidden lg:block", data: { id: entry.id, "bulk-select-target": "row", - action: "bulk-select#toggleRowSelection" + action: "bulk-select#toggleRowSelection", + checkbox_toggle_target: "selectionEntry" } %>
diff --git a/app/views/transactions/index.html.erb b/app/views/transactions/index.html.erb index 47eb8ec81fd..ea9f8b5d9fd 100644 --- a/app/views/transactions/index.html.erb +++ b/app/views/transactions/index.html.erb @@ -46,7 +46,7 @@ <%= render "summary", totals: @search.totals %>
" data-bulk-select-plural-label-value="<%= t(".transactions") %>" class="flex flex-col bg-container rounded-xl shadow-border-xs p-4"> diff --git a/app/views/transactions/searches/_form.html.erb b/app/views/transactions/searches/_form.html.erb index d11fe5cff6a..9ca4bb7a739 100644 --- a/app/views/transactions/searches/_form.html.erb +++ b/app/views/transactions/searches/_form.html.erb @@ -30,5 +30,15 @@ <%= render "transactions/searches/menu", form: form %> <% end %> <% end %> + + <%= button_tag type: "button", + id: "toggle-checkboxes-button", + class: "lg:hidden font-medium whitespace-nowrap inline-flex items-center gap-1 rounded-lg px-3 py-2 text-sm text-primary border border-secondary hover:bg-surface-hover", + data: { + action: "click->checkbox-toggle#toggle", + checkbox_toggle_target: "toggleButton" + } do %> + <%= icon("list-todo") %> + <% end %>
<% end %> From 2cfd431554bc2c1d118ca42ab32b09ed4f35f72b Mon Sep 17 00:00:00 2001 From: Alessio Cappa Date: Sat, 13 Dec 2025 13:43:21 +0100 Subject: [PATCH 02/25] fix: Add multi-select toggle also in activities page. Make JS controller compatible also in this view. --- .../UI/account/activity_date.html.erb | 7 +++++-- .../UI/account/activity_feed.html.erb | 20 +++++++++++++++---- .../controllers/checkbox_toggle_controller.js | 2 +- app/views/accounts/show/_activity.html.erb | 9 ++++++--- app/views/entries/_selection_bar.html.erb | 2 +- app/views/valuations/_valuation.html.erb | 4 ++-- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/app/components/UI/account/activity_date.html.erb b/app/components/UI/account/activity_date.html.erb index 6efa7e967f5..672c6d77c26 100644 --- a/app/components/UI/account/activity_date.html.erb +++ b/app/components/UI/account/activity_date.html.erb @@ -4,9 +4,12 @@
<%= check_box_tag "#{date}_entries_selection", - class: ["checkbox checkbox--light", "hidden": entries.size == 0], + class: ["checkbox checkbox--light hidden lg:block", "lg:hidden": entries.size == 0], id: "selection_entry_#{date}", - data: { action: "bulk-select#toggleGroupSelection" } %> + data: { + action: "bulk-select#toggleGroupSelection", + checkbox_toggle_target: "selectionEntry" + } %>

<%= tag.span I18n.l(date, format: :long) %> diff --git a/app/components/UI/account/activity_feed.html.erb b/app/components/UI/account/activity_feed.html.erb index dd952843054..82d164f9cd9 100644 --- a/app/components/UI/account/activity_feed.html.erb +++ b/app/components/UI/account/activity_feed.html.erb @@ -1,5 +1,5 @@ <%= turbo_frame_tag dom_id(account, "entries") do %> -

+
<%= tag.h2 t("accounts.show.activity.title"), class: "font-medium text-lg" %> @@ -46,8 +46,17 @@ "data-auto-submit-form-target": "auto" %>
-
<% end %> + <%= button_tag type: "button", + id: "toggle-checkboxes-button", + class: "lg:hidden font-medium whitespace-nowrap inline-flex items-center gap-1 rounded-lg px-3 py-2 text-sm text-primary border border-secondary hover:bg-surface-hover", + data: { + action: "click->checkbox-toggle#toggle", + checkbox_toggle_target: "toggleButton" + } do %> + <%= helpers.icon("list-todo") %> + <% end %> +
<% if activity_dates.empty? %> @@ -66,8 +75,11 @@
<%= check_box_tag "selection_entry", - class: "checkbox checkbox--light", - data: { action: "bulk-select#togglePageSelection" } %> + class: "checkbox checkbox--light hidden lg:block", + data: { + action: "bulk-select#togglePageSelection", + checkbox_toggle_target: "selectionEntry" + } %> <%= tag.p t("accounts.show.activity.date") %>
diff --git a/app/javascript/controllers/checkbox_toggle_controller.js b/app/javascript/controllers/checkbox_toggle_controller.js index e4f88dfc6e5..04bb96b9d31 100644 --- a/app/javascript/controllers/checkbox_toggle_controller.js +++ b/app/javascript/controllers/checkbox_toggle_controller.js @@ -15,7 +15,7 @@ export default class extends Controller { }) if (!shouldShow) { - const bulkSelectElement = this.element.closest("[data-controller~='bulk-select']"); + const bulkSelectElement = document.querySelector("[data-controller~='bulk-select']"); if (bulkSelectElement) { const bulkSelectController = this.application.getControllerForElementAndIdentifier( bulkSelectElement, diff --git a/app/views/accounts/show/_activity.html.erb b/app/views/accounts/show/_activity.html.erb index ce9e74cd892..cab1760e098 100644 --- a/app/views/accounts/show/_activity.html.erb +++ b/app/views/accounts/show/_activity.html.erb @@ -55,7 +55,7 @@ <% else %> <%= tag.div id: dom_id(@account, "entries_bulk_select"), data: { - controller: "bulk-select", + controller: "bulk-select checkbox-toggle", bulk_select_singular_label_value: t(".entry"), bulk_select_plural_label_value: t(".entries") } do %> @@ -66,8 +66,11 @@
<%= check_box_tag "selection_entry", - class: "checkbox checkbox--light", - data: { action: "bulk-select#togglePageSelection" } %> + class: "checkbox checkbox--light checkbox checkbox--light hidden lg:block", + data: { + action: "bulk-select#togglePageSelection", + checkbox_toggle_target: "selectionEntry" + } %>

<%= t(".date") %>

<%= tag.p t(".amount"), class: "col-span-2 justify-self-end" %> diff --git a/app/views/entries/_selection_bar.html.erb b/app/views/entries/_selection_bar.html.erb index 48648c2a43a..4c7d8d48b7a 100644 --- a/app/views/entries/_selection_bar.html.erb +++ b/app/views/entries/_selection_bar.html.erb @@ -1,4 +1,4 @@ -
+
<%= check_box_tag "entry_selection", 1, true, class: "checkbox checkbox--light", data: { action: "bulk-select#deselectAll" } %> diff --git a/app/views/valuations/_valuation.html.erb b/app/views/valuations/_valuation.html.erb index 9bb41ce1150..4fbff9cc659 100644 --- a/app/views/valuations/_valuation.html.erb +++ b/app/views/valuations/_valuation.html.erb @@ -10,8 +10,8 @@
<%= check_box_tag dom_id(entry, "selection"), - class: "checkbox checkbox--light", - data: { id: entry.id, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection" } %> + class: "checkbox checkbox--light hidden lg:block", + data: { id: entry.id, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection", checkbox_toggle_target: "selectionEntry" } %>
<%= render DS::FilledIcon.new(icon: icon, size: "sm", hex_color: color, rounded: true) %> From 36bec8e40cb82bb9d2d9ffe4bc8cf6c14999e828 Mon Sep 17 00:00:00 2001 From: Alessio Cappa Date: Sat, 13 Dec 2025 13:43:21 +0100 Subject: [PATCH 03/25] feat: Add category in mobile view --- .../transaction_categories_controller.rb | 5 ++++ app/views/categories/_badge_mobile.html.erb | 14 +++++++++ .../categories/_category_name_mobile.html.erb | 3 ++ app/views/categories/_menu.html.erb | 7 ++++- app/views/category/dropdowns/_row.html.erb | 1 + app/views/transactions/_transaction.html.erb | 30 ++++++++++++------- 6 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 app/views/categories/_badge_mobile.html.erb create mode 100644 app/views/categories/_category_name_mobile.html.erb diff --git a/app/controllers/transaction_categories_controller.rb b/app/controllers/transaction_categories_controller.rb index dac6b5be0d6..6ca3aa70cad 100644 --- a/app/controllers/transaction_categories_controller.rb +++ b/app/controllers/transaction_categories_controller.rb @@ -27,6 +27,11 @@ def update partial: "categories/menu", locals: { transaction: transaction } ), + turbo_stream.replace( + "category_name_mobile_#{transaction.id}", + partial: "categories/category_name_mobile", + locals: { transaction: transaction } + ), *flash_notification_stream_items ] end diff --git a/app/views/categories/_badge_mobile.html.erb b/app/views/categories/_badge_mobile.html.erb new file mode 100644 index 00000000000..4f96f25b4bc --- /dev/null +++ b/app/views/categories/_badge_mobile.html.erb @@ -0,0 +1,14 @@ +<%# locals: (category:) %> +<% category ||= Category.uncategorized %> + +
+ + <% if category.lucide_icon.present? %> + <%= icon category.lucide_icon, size: "sm", color: "current" %> + <% end %> + +
\ No newline at end of file diff --git a/app/views/categories/_category_name_mobile.html.erb b/app/views/categories/_category_name_mobile.html.erb new file mode 100644 index 00000000000..febe4b15a65 --- /dev/null +++ b/app/views/categories/_category_name_mobile.html.erb @@ -0,0 +1,3 @@ + + <%= transaction.category&.name %> + diff --git a/app/views/categories/_menu.html.erb b/app/views/categories/_menu.html.erb index f7c74eaca44..ad7dc4f5c4a 100644 --- a/app/views/categories/_menu.html.erb +++ b/app/views/categories/_menu.html.erb @@ -2,7 +2,12 @@ <%= render DS::Menu.new(variant: "button") do |menu| %> <% menu.with_button do %> - <% render partial: "categories/badge", locals: { category: transaction.category } %> + +
+ <%= render partial: "categories/badge_mobile", locals: { category: transaction.category } %> +
<% end %> <% menu.with_custom_content do %> diff --git a/app/views/category/dropdowns/_row.html.erb b/app/views/category/dropdowns/_row.html.erb index 6e25d478f25..0a4927dcd7f 100644 --- a/app/views/category/dropdowns/_row.html.erb +++ b/app/views/category/dropdowns/_row.html.erb @@ -13,6 +13,7 @@ } ), method: :patch, + data: { turbo_frame: "category_dropdown" }, class: "flex w-full items-center gap-1.5 cursor-pointer focus:outline-none" do %> <%= icon("check") if is_selected %> diff --git a/app/views/transactions/_transaction.html.erb b/app/views/transactions/_transaction.html.erb index 86dd0e193bd..2a0d70cdf7d 100644 --- a/app/views/transactions/_transaction.html.erb +++ b/app/views/transactions/_transaction.html.erb @@ -24,12 +24,17 @@ class: "w-6 h-6 rounded-full", loading: "lazy" %> <% else %> - <%= render DS::FilledIcon.new( - variant: :text, - text: entry.name, - size: "sm", - rounded: true - ) %> + +
+ <%= render "transactions/transaction_category", transaction: transaction %> +
<% end %>
@@ -72,16 +77,19 @@
- From 48a2e47abf831fa6bbd9d4f17dc03037e6a14efc Mon Sep 17 00:00:00 2001 From: Alessio Cappa Date: Sat, 13 Dec 2025 13:43:21 +0100 Subject: [PATCH 04/25] feat: Add mobile layout for transaction categories --- app/views/categories/_category_name_mobile.html.erb | 6 +++++- app/views/transactions/_transaction_category.html.erb | 7 ++++++- app/views/transactions/_transfer_match.html.erb | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/views/categories/_category_name_mobile.html.erb b/app/views/categories/_category_name_mobile.html.erb index febe4b15a65..c0fd418ef46 100644 --- a/app/views/categories/_category_name_mobile.html.erb +++ b/app/views/categories/_category_name_mobile.html.erb @@ -1,3 +1,7 @@ - <%= transaction.category&.name %> + <% if transaction.transfer&.categorizable? || transaction.transfer.nil? %> + <%= transaction.category.name %> + <% else %> + <%= transaction.transfer&.payment? ? payment_category.name : transfer_category.name %> + <% end %> diff --git a/app/views/transactions/_transaction_category.html.erb b/app/views/transactions/_transaction_category.html.erb index 933a80feab5..e124c02139c 100644 --- a/app/views/transactions/_transaction_category.html.erb +++ b/app/views/transactions/_transaction_category.html.erb @@ -4,6 +4,11 @@ <% if transaction.transfer&.categorizable? || transaction.transfer.nil? %> <%= render "categories/menu", transaction: transaction %> <% else %> - <%= render "categories/badge", category: transaction.transfer&.payment? ? payment_category : transfer_category %> + +
+ <%= render "categories/badge_mobile", category: transaction.transfer&.payment? ? payment_category : transfer_category %> +
<% end %>
diff --git a/app/views/transactions/_transfer_match.html.erb b/app/views/transactions/_transfer_match.html.erb index 1b3a6df788d..53d05a4f47f 100644 --- a/app/views/transactions/_transfer_match.html.erb +++ b/app/views/transactions/_transfer_match.html.erb @@ -6,9 +6,12 @@ <%= icon "link-2", size: "sm", class: "text-secondary" %> <% elsif transaction.transfer.pending? %> - + + + A/M + <%= button_to transfer_path(transaction.transfer, transfer: { status: "confirmed" }), method: :patch, From b170d4f2cbd97bc89ed264dd96db16b8e96bb3c8 Mon Sep 17 00:00:00 2001 From: Alessio Cappa Date: Sat, 13 Dec 2025 13:43:21 +0100 Subject: [PATCH 05/25] feat: Add margin for pagination on mobile --- app/views/shared/_pagination.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_pagination.html.erb b/app/views/shared/_pagination.html.erb index afc047de533..c3c9c292a9e 100644 --- a/app/views/shared/_pagination.html.erb +++ b/app/views/shared/_pagination.html.erb @@ -1,7 +1,7 @@ <%# locals: (pagy:) %>