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..1cdf7baa 100644 --- a/app/controllers/concerns/accountable_resource.rb +++ b/app/controllers/concerns/accountable_resource.rb @@ -2,6 +2,8 @@ module AccountableResource extend ActiveSupport::Concern included do + include ScrollFocusable + layout :with_sidebar before_action :set_account, only: [ :show, :edit, :update, :destroy ] before_action :set_link_token, only: :new @@ -22,6 +24,12 @@ module AccountableResource end def show + @q = params.fetch(:q, {}).permit(:search) + entries = @account.entries.search(@q).reverse_chronological + + set_focused_record(entries, params[:focused_record_id]) + + @pagy, @entries = pagy(entries, limit: params[:per_page] || "10", params: ->(params) { params.except(:focused_record_id) }) end def edit diff --git a/app/controllers/concerns/scroll_focusable.rb b/app/controllers/concerns/scroll_focusable.rb new file mode 100644 index 00000000..7eb47a1b --- /dev/null +++ b/app/controllers/concerns/scroll_focusable.rb @@ -0,0 +1,21 @@ +module ScrollFocusable + extend ActiveSupport::Concern + + def set_focused_record(record_scope, record_id, default_per_page: 10) + return unless record_id.present? + + @focused_record = record_scope.find_by(id: record_id) + + record_index = record_scope.pluck(:id).index(record_id) + + return unless record_index + + page_of_focused_record = (record_index / (params[:per_page]&.to_i || default_per_page)) + 1 + + if params[:page]&.to_i != page_of_focused_record + ( + redirect_to(url_for(page: page_of_focused_record, focused_record_id: record_id)) + ) + end + end +end diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb index 80248ef2..7478894b 100644 --- a/app/controllers/transactions_controller.rb +++ b/app/controllers/transactions_controller.rb @@ -1,10 +1,21 @@ class TransactionsController < ApplicationController + include ScrollFocusable + layout :with_sidebar + 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] || "50") + + set_focused_record(search_query, params[:focused_record_id], default_per_page: 50) + + @pagy, @transaction_entries = pagy( + search_query, + limit: params[:per_page].presence || default_params[:per_page], + params: ->(params) { params.except(:focused_record_id) } + ) totals_query = search_query.incomes_and_expenses family_currency = Current.family.currency @@ -18,13 +29,76 @@ class TransactionsController < ApplicationController } 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! + 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 + + 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/helpers/transactions_helper.rb b/app/helpers/transactions_helper.rb index 5c6f4d7b..dd729c47 100644 --- a/app/helpers/transactions_helper.rb +++ b/app/helpers/transactions_helper.rb @@ -18,21 +18,4 @@ 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 diff --git a/app/javascript/controllers/focus_record_controller.js b/app/javascript/controllers/focus_record_controller.js new file mode 100644 index 00000000..0cc3fc9a --- /dev/null +++ b/app/javascript/controllers/focus_record_controller.js @@ -0,0 +1,21 @@ +import { Controller } from "@hotwired/stimulus"; + +// Connects to data-controller="focus-record" +export default class extends Controller { + static values = { + id: String, + }; + + connect() { + const element = document.getElementById(this.idValue); + + if (element) { + element.scrollIntoView({ behavior: "smooth" }); + + // Remove the focused_record_id parameter from URL + const url = new URL(window.location); + url.searchParams.delete("focused_record_id"); + window.history.replaceState({}, "", url); + } + } +} 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 deleted file mode 100644 index 1ac6de61..00000000 --- a/app/views/account/entries/index.html.erb +++ /dev/null @@ -1,93 +0,0 @@ -<%= turbo_frame_tag dom_id(@account, "entries") do %> -
<%= 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 %> - - -<%= t(".date") %>
-<%= 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 %> + + +<%= t(".date") %>
+