From 5cb3b7fc75e667c26656c72a993d40b38378babd Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Tue, 28 Jan 2025 19:47:20 -0500 Subject: [PATCH 1/9] Preserve transaction filters across page visits --- app/controllers/transactions_controller.rb | 78 ++++++++++++++++++- app/helpers/transactions_helper.rb | 19 +---- .../transactions/searches/_menu.html.erb | 2 +- .../searches/filters/_badge.html.erb | 2 +- config/routes.rb | 7 +- ...03_store_transaction_filters_in_session.rb | 5 ++ db/schema.rb | 3 +- 7 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 db/migrate/20250128203303_store_transaction_filters_in_session.rb diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb index 80248ef2..1628f358 100644 --- a/app/controllers/transactions_controller.rb +++ b/app/controllers/transactions_controller.rb @@ -1,10 +1,17 @@ class TransactionsController < ApplicationController layout :with_sidebar + before_action :restore_params_and_redirect!, only: :index, if: :should_restore_params? + def index - @q = search_params + @q = search_params || {} + @page = (params[:page] || 1).to_i + @per_page = (params[:per_page] || 50).to_i + + store_params!(@q, @page, @per_page) if @q.present? || @page > 1 || @per_page != 50 + search_query = Current.family.transactions.search(@q).reverse_chronological - @pagy, @transaction_entries = pagy(search_query, limit: params[:per_page] || "50") + @pagy, @transaction_entries = pagy(search_query, limit: @per_page) totals_query = search_query.incomes_and_expenses family_currency = Current.family.currency @@ -18,13 +25,78 @@ class TransactionsController < ApplicationController } end + def clear_filters + Current.session.update!(prev_transaction_page_params: {}) + redirect_to transactions_path + end + + def clear_filter_params + params.permit(:param_key, :param_value) + end + + def clear_filter + updated_params = stored_params.deep_dup + + q_params = updated_params["q"] || {} + + param_key = params[:param_key] + param_value = params[:param_value] + + if q_params[param_key].is_a?(Array) + q_params[param_key].delete(param_value) + q_params.delete(param_key) if q_params[param_key].empty? + else + q_params.delete(param_key) + end + + updated_params["q"] = q_params.presence + Current.session.update!(prev_transaction_page_params: updated_params) + + redirect_to transactions_path(updated_params) + end + private def search_params - params.fetch(:q, {}) + cleaned_params = params.fetch(:q, {}) .permit( :start_date, :end_date, :search, :amount, :amount_operator, accounts: [], account_ids: [], categories: [], merchants: [], types: [], tags: [] ) + .to_h + .compact_blank + + cleaned_params.delete(:amount_operator) unless cleaned_params[:amount].present? + + cleaned_params + end + + def store_params!(q, page, per_page) + Current.session.update!( + prev_transaction_page_params: { + q: q, + page: page, + per_page: per_page + } + ) + end + + def stored_params + Current.session.prev_transaction_page_params + end + + def should_restore_params? + request.query_parameters.blank? && (stored_params["q"].present? || stored_params["page"].to_i > 1 || stored_params["per_page"].to_i != 50) + end + + def restore_params_and_redirect! + page_value = stored_params["page"].to_i == 1 ? nil : stored_params["page"] + per_page_value = stored_params["per_page"].to_i == 50 ? nil : stored_params["per_page"] + + redirect_to transactions_path( + q: stored_params["q"], + page: page_value, + per_page: per_page_value + ) end end diff --git a/app/helpers/transactions_helper.rb b/app/helpers/transactions_helper.rb index 5c6f4d7b..08e0004f 100644 --- a/app/helpers/transactions_helper.rb +++ b/app/helpers/transactions_helper.rb @@ -17,22 +17,5 @@ module TransactionsHelper def get_default_transaction_search_filter transaction_search_filters[0] - end - - def transactions_path_without_param(param_key, param_value) - updated_params = request.query_parameters.deep_dup - - q_params = updated_params[:q] || {} - - current_value = q_params[param_key] - if current_value.is_a?(Array) - q_params[param_key] = current_value - [ param_value ] - else - q_params.delete(param_key) - end - - updated_params[:q] = q_params - - transactions_path(updated_params) - end + end end diff --git a/app/views/transactions/searches/_menu.html.erb b/app/views/transactions/searches/_menu.html.erb index a42840ef..72ff96c9 100644 --- a/app/views/transactions/searches/_menu.html.erb +++ b/app/views/transactions/searches/_menu.html.erb @@ -33,7 +33,7 @@
<% if @q.present? %> - <%= link_to t(".clear_filters"), transactions_path, class: "btn btn--ghost" %> + <%= button_to t(".clear_filters"), clear_filters_transactions_path, method: :delete, class: "btn btn--ghost" %> <% end %>
diff --git a/app/views/transactions/searches/filters/_badge.html.erb b/app/views/transactions/searches/filters/_badge.html.erb index f0ec8b1a..1fd2da28 100644 --- a/app/views/transactions/searches/filters/_badge.html.erb +++ b/app/views/transactions/searches/filters/_badge.html.erb @@ -41,7 +41,7 @@
<% end %> - <%= link_to transactions_path_without_param(param_key, param_value), data: { id: "clear-param-btn", turbo: false }, class: "flex items-center" do %> + <%= button_to clear_filter_transactions_path(param_key: param_key, param_value: param_value), method: :delete, data: { turbo: false }, class: "flex items-center" do %> <%= lucide_icon "x", class: "w-4 h-4 text-gray-500" %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index d49ca187..dcbb00be 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -109,7 +109,12 @@ Rails.application.routes.draw do end end - resources :transactions, only: :index + resources :transactions, only: :index do + collection do + delete :clear_filters + delete :clear_filter + end + end # Convenience routes for polymorphic paths # Example: account_path(Account.new(accountable: Depository.new)) => /depositories/123 diff --git a/db/migrate/20250128203303_store_transaction_filters_in_session.rb b/db/migrate/20250128203303_store_transaction_filters_in_session.rb new file mode 100644 index 00000000..00c7ee5c --- /dev/null +++ b/db/migrate/20250128203303_store_transaction_filters_in_session.rb @@ -0,0 +1,5 @@ +class StoreTransactionFiltersInSession < ActiveRecord::Migration[7.2] + def change + add_column :sessions, :prev_transaction_page_params, :jsonb, default: {} + end +end diff --git a/db/schema.rb b/db/schema.rb index 5b0a0697..54a65580 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_01_24_224316) do +ActiveRecord::Schema[7.2].define(version: 2025_01_28_203303) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -569,6 +569,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_24_224316) do t.datetime "updated_at", null: false t.uuid "active_impersonator_session_id" t.datetime "subscribed_at" + t.jsonb "prev_transaction_page_params", default: {} t.index ["active_impersonator_session_id"], name: "index_sessions_on_active_impersonator_session_id" t.index ["user_id"], name: "index_sessions_on_user_id" end -- 2.53.0 From af6f12bb033e256455012e6116be2173f83785d1 Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Wed, 29 Jan 2025 15:06:20 -0500 Subject: [PATCH 2/9] Preserve params when per_page is updated --- app/controllers/transactions_controller.rb | 65 +++++++++---------- app/helpers/application_helper.rb | 20 ------ .../controllers/selectable_link_controller.js | 20 ++++++ app/views/account/entries/index.html.erb | 2 +- app/views/application/_pagination.html.erb | 37 ++++------- .../transactions/searches/_form.html.erb | 2 + 6 files changed, 68 insertions(+), 78 deletions(-) create mode 100644 app/javascript/controllers/selectable_link_controller.js diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb index 1628f358..56bcad6c 100644 --- a/app/controllers/transactions_controller.rb +++ b/app/controllers/transactions_controller.rb @@ -1,17 +1,13 @@ class TransactionsController < ApplicationController layout :with_sidebar - before_action :restore_params_and_redirect!, only: :index, if: :should_restore_params? + # before_action :store_params!, only: :index def index - @q = search_params || {} - @page = (params[:page] || 1).to_i - @per_page = (params[:per_page] || 50).to_i - - store_params!(@q, @page, @per_page) if @q.present? || @page > 1 || @per_page != 50 + @q = search_params search_query = Current.family.transactions.search(@q).reverse_chronological - @pagy, @transaction_entries = pagy(search_query, limit: @per_page) + @pagy, @transaction_entries = pagy(search_query, limit: params[:per_page].presence || default_params[:per_page]) totals_query = search_query.incomes_and_expenses family_currency = Current.family.currency @@ -30,10 +26,6 @@ class TransactionsController < ApplicationController redirect_to transactions_path end - def clear_filter_params - params.permit(:param_key, :param_value) - end - def clear_filter updated_params = stored_params.deep_dup @@ -71,32 +63,39 @@ class TransactionsController < ApplicationController cleaned_params end - def store_params!(q, page, per_page) - Current.session.update!( - prev_transaction_page_params: { - q: q, - page: page, - per_page: per_page - } - ) + def store_params! + if should_restore_params? + params_to_restore = {} + + params_to_restore[:q] = stored_params["q"].presence || default_params[:q] + params_to_restore[:page] = stored_params["page"].presence || default_params[:page] + params_to_restore[:per_page] = stored_params["per_page"].presence || default_params[:per_page] + + redirect_to transactions_path(params_to_restore) + else + Current.session.update!( + prev_transaction_page_params: { + q: search_params, + page: params[:page], + per_page: params[:per_page] + } + ) + end + end + + def should_restore_params? + request.query_parameters.blank? && (stored_params["q"].present? || stored_params["page"].present? || stored_params["per_page"].present?) end def stored_params Current.session.prev_transaction_page_params - end + end - def should_restore_params? - request.query_parameters.blank? && (stored_params["q"].present? || stored_params["page"].to_i > 1 || stored_params["per_page"].to_i != 50) - end - - def restore_params_and_redirect! - page_value = stored_params["page"].to_i == 1 ? nil : stored_params["page"] - per_page_value = stored_params["per_page"].to_i == 50 ? nil : stored_params["per_page"] - - redirect_to transactions_path( - q: stored_params["q"], - page: page_value, - per_page: per_page_value - ) + def default_params + { + q: {}, + page: 1, + per_page: 50 + } end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4f1c9499..5d561bc1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -176,24 +176,4 @@ module ApplicationHelper cookies[:admin] == "true" end - - def custom_pagy_url_for(pagy, page, current_path: nil) - if current_path.blank? - pagy_url_for(pagy, page) - else - uri = URI.parse(current_path) - params = URI.decode_www_form(uri.query || "").to_h - - # Delete existing page param if it exists - params.delete("page") - # Add new page param unless it's page 1 - params["page"] = page unless page == 1 - - if params.empty? - uri.path - else - "#{uri.path}?#{URI.encode_www_form(params)}" - end - end - end end diff --git a/app/javascript/controllers/selectable_link_controller.js b/app/javascript/controllers/selectable_link_controller.js new file mode 100644 index 00000000..7d93a7ce --- /dev/null +++ b/app/javascript/controllers/selectable_link_controller.js @@ -0,0 +1,20 @@ +import { Controller } from "@hotwired/stimulus"; + +// Connects to data-controller="selectable-link" +export default class extends Controller { + connect() { + this.element.addEventListener("change", this.handleChange.bind(this)); + } + + disconnect() { + this.element.removeEventListener("change", this.handleChange.bind(this)); + } + + handleChange(event) { + const paramName = this.element.name; + const currentUrl = new URL(window.location.href); + currentUrl.searchParams.set(paramName, event.target.value); + + Turbo.visit(currentUrl.toString()); + } +} diff --git a/app/views/account/entries/index.html.erb b/app/views/account/entries/index.html.erb index 1ac6de61..c659d97f 100644 --- a/app/views/account/entries/index.html.erb +++ b/app/views/account/entries/index.html.erb @@ -84,7 +84,7 @@
- <%= render "pagination", pagy: @pagy, current_path: account_path(@account, page: params[:page], tab: params[:tab]) %> + <%= render "pagination", pagy: @pagy %>
<% end %> diff --git a/app/views/application/_pagination.html.erb b/app/views/application/_pagination.html.erb index 13da2cd9..32809bcb 100644 --- a/app/views/application/_pagination.html.erb +++ b/app/views/application/_pagination.html.erb @@ -1,11 +1,10 @@ -<%# locals: (pagy:, current_path: nil) %> +<%# locals: (pagy:) %> diff --git a/app/views/transactions/searches/_form.html.erb b/app/views/transactions/searches/_form.html.erb index 84f628b3..2e6ad04c 100644 --- a/app/views/transactions/searches/_form.html.erb +++ b/app/views/transactions/searches/_form.html.erb @@ -3,6 +3,8 @@ scope: :q, method: :get, data: { controller: "auto-submit-form" } do |form| %> + <%= hidden_field_tag :per_page, params[:per_page] %> +
-- 2.53.0 From 59ec17e6415db693b3c9053d3b3a773ac5e9d9e9 Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Wed, 29 Jan 2025 16:26:58 -0500 Subject: [PATCH 3/9] Autofocus selected transactions --- app/controllers/account/entries_controller.rb | 26 ----- .../concerns/accountable_resource.rb | 16 ++++ app/controllers/transactions_controller.rb | 20 +++- .../controllers/focus_entry_controller.js | 29 ++++++ app/views/account/entries/index.html.erb | 93 ------------------ .../transactions/_transaction.html.erb | 4 +- app/views/accounts/show/_activity.html.erb | 94 ++++++++++++++++++- app/views/application/_pagination.html.erb | 5 + app/views/credit_cards/show.html.erb | 2 +- app/views/investments/show.html.erb | 2 +- app/views/loans/show.html.erb | 2 +- app/views/properties/show.html.erb | 2 +- app/views/transactions/index.html.erb | 2 +- app/views/vehicles/show.html.erb | 2 +- config/locales/models/transfer/en.yml | 9 +- config/locales/views/account/entries/en.yml | 11 --- config/locales/views/accounts/en.yml | 11 +++ config/locales/views/application/en.yml | 3 - config/locales/views/categories/en.yml | 4 +- config/locales/views/layout/en.yml | 2 +- config/routes.rb | 2 - .../account/entries_controller_test.rb | 13 --- 22 files changed, 186 insertions(+), 168 deletions(-) delete mode 100644 app/controllers/account/entries_controller.rb create mode 100644 app/javascript/controllers/focus_entry_controller.js delete mode 100644 app/views/account/entries/index.html.erb delete mode 100644 test/controllers/account/entries_controller_test.rb diff --git a/app/controllers/account/entries_controller.rb b/app/controllers/account/entries_controller.rb deleted file mode 100644 index b36cdbc6..00000000 --- a/app/controllers/account/entries_controller.rb +++ /dev/null @@ -1,26 +0,0 @@ -class Account::EntriesController < ApplicationController - layout :with_sidebar - - before_action :set_account - - def index - @q = search_params - @pagy, @entries = pagy(entries_scope.search(@q).reverse_chronological, limit: params[:per_page] || "10") - end - - private - def set_account - @account = Current.family.accounts.find(params[:account_id]) - end - - def entries_scope - scope = Current.family.entries - scope = scope.where(account: @account) if @account - scope - end - - def search_params - params.fetch(:q, {}) - .permit(:search) - end -end diff --git a/app/controllers/concerns/accountable_resource.rb b/app/controllers/concerns/accountable_resource.rb index 8f6a3244..0c2b554b 100644 --- a/app/controllers/concerns/accountable_resource.rb +++ b/app/controllers/concerns/accountable_resource.rb @@ -22,6 +22,22 @@ module AccountableResource end def show + @q = params.fetch(:q, {}).permit(:search) + entries = @account.entries.search(@q).reverse_chronological + + if params[:focused_entry_id].present? + @focused_entry = entries.find_by(id: params[:focused_entry_id]) + position = entries.pluck(:id).index(params[:focused_entry_id]) + + if position.present? + focused_page = (position / (params[:per_page] || 10)) + 1 + if params[:page]&.to_i != focused_page + return redirect_to account_path(@account, page: focused_page, focused_entry_id: params[:focused_entry_id]) + end + end + end + + @pagy, @entries = pagy(entries, limit: params[:per_page] || "10", params: ->(params) { params.except(:focused_entry_id) }) end def edit diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb index 56bcad6c..06bfe5c7 100644 --- a/app/controllers/transactions_controller.rb +++ b/app/controllers/transactions_controller.rb @@ -1,13 +1,27 @@ class TransactionsController < ApplicationController layout :with_sidebar - # before_action :store_params!, only: :index + before_action :store_params!, only: :index def index @q = search_params - search_query = Current.family.transactions.search(@q).reverse_chronological - @pagy, @transaction_entries = pagy(search_query, limit: params[:per_page].presence || default_params[:per_page]) + + if params[:focused_entry_id].present? + @focused_entry = search_query.find_by(id: params[:focused_entry_id]) + position = search_query.pluck(:id).index(params[:focused_entry_id]) + + if position.present? + focused_page = (position / (params[:per_page] || 10).to_i) + 1 + if params[:page]&.to_i != focused_page + return redirect_to transactions_path(page: focused_page, focused_entry_id: params[:focused_entry_id]) + else + params.delete(:focused_entry_id) + end + end + end + + @pagy, @transaction_entries = pagy(search_query, limit: params[:per_page].presence || default_params[:per_page], params: ->(params) { params.except(:focused_entry_id) }) totals_query = search_query.incomes_and_expenses family_currency = Current.family.currency diff --git a/app/javascript/controllers/focus_entry_controller.js b/app/javascript/controllers/focus_entry_controller.js new file mode 100644 index 00000000..48ed5044 --- /dev/null +++ b/app/javascript/controllers/focus_entry_controller.js @@ -0,0 +1,29 @@ +import { Controller } from "@hotwired/stimulus"; + +// Connects to data-controller="focus-entry" +export default class extends Controller { + static values = { + id: String, + }; + + connect() { + if (this.idValue) { + // Wait for any Turbo navigation to complete + document.addEventListener( + "turbo:load", + () => { + const element = document.getElementById(this.idValue); + if (element) { + element.scrollIntoView({ behavior: "smooth" }); + + // Remove the focused_entry_id parameter from URL + const url = new URL(window.location); + url.searchParams.delete("focused_entry_id"); + window.history.replaceState({}, "", url); + } + }, + { once: true }, + ); + } + } +} diff --git a/app/views/account/entries/index.html.erb b/app/views/account/entries/index.html.erb deleted file mode 100644 index c659d97f..00000000 --- a/app/views/account/entries/index.html.erb +++ /dev/null @@ -1,93 +0,0 @@ -<%= turbo_frame_tag dom_id(@account, "entries") do %> -
-
- <%= tag.h2 t(".title"), class: "font-medium text-lg" %> - <% unless @account.plaid_account_id.present? %> -
- - -
- <% end %> -
- -
- <%= form_with url: account_entries_path, - id: "entries-search", - scope: :q, - method: :get, - data: { controller: "auto-submit-form" } do |form| %> -
-
-
- <%= lucide_icon("search", class: "w-5 h-5 text-gray-500") %> - <%= hidden_field_tag :account_id, @account.id %> - <%= form.search_field :search, - placeholder: "Search entries by name", - value: @q[:search], - class: "form-field__input placeholder:text-sm placeholder:text-gray-500", - "data-auto-submit-form-target": "auto" %> -
-
-
- <% end %> -
- - <% if @entries.empty? %> -

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

- <% else %> - <%= tag.div id: dom_id(@account, "entries_bulk_select"), - data: { - controller: "bulk-select", - bulk_select_singular_label_value: t(".entry"), - bulk_select_plural_label_value: t(".entries") - } do %> - - -
-
- <%= check_box_tag "selection_entry", - class: "maybe-checkbox maybe-checkbox--light", - data: { action: "bulk-select#togglePageSelection" } %> -

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

-
- <%= tag.p t(".amount"), class: "col-span-2 justify-self-end" %> - <%= tag.p t(".balance"), class: "col-span-2 justify-self-end" %> -
- -
-
-
- <% calculator = Account::BalanceTrendCalculator.for(@entries) %> - <%= entries_by_date(@entries) do |entries| %> - <% entries.each do |entry| %> - <%= render entry, balance_trend: calculator&.trend_for(entry) %> - <% end %> - <% end %> -
-
- -
- <%= render "pagination", pagy: @pagy %> -
-
- <% end %> - <% end %> -
-<% end %> diff --git a/app/views/account/transactions/_transaction.html.erb b/app/views/account/transactions/_transaction.html.erb index ce6cd187..d6c0201b 100644 --- a/app/views/account/transactions/_transaction.html.erb +++ b/app/views/account/transactions/_transaction.html.erb @@ -1,7 +1,7 @@ <%# locals: (entry:, selectable: true, balance_trend: nil) %> <% transaction, account = entry.account_transaction, entry.account %> -
+
">
"> <% if selectable %> <%= check_box_tag dom_id(entry, "selection"), @@ -45,7 +45,7 @@ <% if entry.account_transaction.transfer? %> <%= render "transfers/account_links", transfer: entry.account_transaction.transfer, is_inflow: entry.account_transaction.transfer_as_inflow.present? %> <% else %> - <%= link_to entry.account.name, account_path(entry.account, tab: "transactions"), data: { turbo_frame: "_top" }, class: "hover:underline" %> + <%= link_to entry.account.name, account_path(entry.account, tab: "transactions", focused_entry_id: entry.id), data: { turbo_frame: "_top" }, class: "hover:underline" %> <% end %>
diff --git a/app/views/accounts/show/_activity.html.erb b/app/views/accounts/show/_activity.html.erb index 86dc7038..715e9dfc 100644 --- a/app/views/accounts/show/_activity.html.erb +++ b/app/views/accounts/show/_activity.html.erb @@ -1,5 +1,95 @@ <%# locals: (account:) %> -<%= turbo_frame_tag dom_id(account, :entries), src: account_entries_path(account_id: account.id, page: params[:page], tab: params[:tab]) do %> - <%= render "account/entries/loading" %> +<%= turbo_frame_tag dom_id(account, "entries") do %> +
+
+ <%= tag.h2 t(".title"), class: "font-medium text-lg" %> + <% unless @account.plaid_account_id.present? %> +
+ + +
+ <% end %> +
+ +
+ <%= form_with url: account_path(account), + id: "entries-search", + scope: :q, + method: :get, + data: { controller: "auto-submit-form" } do |form| %> +
+
+
+ <%= lucide_icon("search", class: "w-5 h-5 text-gray-500") %> + <%= hidden_field_tag :account_id, @account.id %> + <%= form.search_field :search, + placeholder: "Search entries by name", + value: @q[:search], + class: "form-field__input placeholder:text-sm placeholder:text-gray-500", + "data-auto-submit-form-target": "auto" %> +
+
+
+ <% end %> +
+ + <% if @entries.empty? %> +

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

+ <% else %> + <%= tag.div id: dom_id(@account, "entries_bulk_select"), + data: { + controller: "bulk-select", + bulk_select_singular_label_value: t(".entry"), + bulk_select_plural_label_value: t(".entries") + } do %> + + +
+
+ <%= check_box_tag "selection_entry", + class: "maybe-checkbox maybe-checkbox--light", + data: { action: "bulk-select#togglePageSelection" } %> +

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

+
+ <%= tag.p t(".amount"), class: "col-span-2 justify-self-end" %> + <%= tag.p t(".balance"), class: "col-span-2 justify-self-end" %> +
+ +
+
+
+ <% calculator = Account::BalanceTrendCalculator.for(@entries) %> + <%= entries_by_date(@entries) do |entries| %> + <% entries.each do |entry| %> + <%= render entry, balance_trend: calculator&.trend_for(entry) %> + <% end %> + <% end %> +
+
+ +
+ <%= render "pagination", pagy: @pagy %> +
+
+ <% end %> + <% end %> +
<% end %> diff --git a/app/views/application/_pagination.html.erb b/app/views/application/_pagination.html.erb index 32809bcb..d6d8099b 100644 --- a/app/views/application/_pagination.html.erb +++ b/app/views/application/_pagination.html.erb @@ -1,9 +1,11 @@ <%# locals: (pagy:) %> +