mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-04 21:15:19 +02:00
Preserve transaction filters and transaction focus across page visits (#1733)
* Preserve transaction filters across page visits * Preserve params when per_page is updated * Autofocus selected transactions * Lint fixes * Fix syntax error * Fix filter clearing * Update e2e tests for new UI * Consolidate focus behavior into concern * Lint fixes
This commit is contained in:
parent
0b17976256
commit
282c05345d
34 changed files with 310 additions and 243 deletions
|
@ -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
|
|
@ -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
|
||||
|
|
21
app/controllers/concerns/scroll_focusable.rb
Normal file
21
app/controllers/concerns/scroll_focusable.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
21
app/javascript/controllers/focus_record_controller.js
Normal file
21
app/javascript/controllers/focus_record_controller.js
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
20
app/javascript/controllers/selectable_link_controller.js
Normal file
20
app/javascript/controllers/selectable_link_controller.js
Normal file
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<%= turbo_frame_tag dom_id(@account, "entries") do %>
|
||||
<div class="bg-white p-5 border border-alpha-black-25 rounded-xl shadow-xs">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<%= tag.h2 t(".title"), class: "font-medium text-lg" %>
|
||||
<% unless @account.plaid_account_id.present? %>
|
||||
<div data-controller="menu" data-testid="activity-menu">
|
||||
<button class="btn btn--secondary flex items-center gap-2" data-menu-target="button">
|
||||
<%= lucide_icon("plus", class: "w-4 h-4") %>
|
||||
<%= tag.span t(".new") %>
|
||||
</button>
|
||||
<div data-menu-target="content" class="z-10 hidden bg-white rounded-lg border border-alpha-black-25 shadow-xs p-1">
|
||||
<%= link_to new_account_valuation_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %>
|
||||
<%= lucide_icon("circle-dollar-sign", class: "text-gray-500 w-5 h-5") %>
|
||||
<%= tag.span t(".new_balance"), class: "text-sm" %>
|
||||
<% end %>
|
||||
|
||||
<% unless @account.crypto? %>
|
||||
<%= link_to @account.investment? ? new_account_trade_path(account_id: @account.id) : new_account_transaction_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %>
|
||||
<%= lucide_icon("credit-card", class: "text-gray-500 w-5 h-5") %>
|
||||
<%= tag.span t(".new_transaction"), class: "text-sm" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= form_with url: account_entries_path,
|
||||
id: "entries-search",
|
||||
scope: :q,
|
||||
method: :get,
|
||||
data: { controller: "auto-submit-form" } do |form| %>
|
||||
<div class="flex gap-2 mb-4">
|
||||
<div class="grow">
|
||||
<div class="flex items-center px-3 py-2 gap-2 border border-gray-200 rounded-lg focus-within:ring-gray-100 focus-within:border-gray-900">
|
||||
<%= 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" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @entries.empty? %>
|
||||
<p class="text-gray-500 text-sm p-4"><%= t(".no_entries") %></p>
|
||||
<% 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 %>
|
||||
<div id="entry-selection-bar" data-bulk-select-target="selectionBar" class="flex justify-center hidden">
|
||||
<%= render "account/entries/selection_bar" %>
|
||||
</div>
|
||||
|
||||
<div class="grid bg-gray-25 rounded-xl grid-cols-12 items-center uppercase text-xs font-medium text-gray-500 px-5 py-3 mb-4">
|
||||
<div class="pl-0.5 col-span-8 flex items-center gap-4">
|
||||
<%= check_box_tag "selection_entry",
|
||||
class: "maybe-checkbox maybe-checkbox--light",
|
||||
data: { action: "bulk-select#togglePageSelection" } %>
|
||||
<p><%= t(".date") %></p>
|
||||
</div>
|
||||
<%= tag.p t(".amount"), class: "col-span-2 justify-self-end" %>
|
||||
<%= tag.p t(".balance"), class: "col-span-2 justify-self-end" %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="rounded-tl-lg rounded-tr-lg bg-white border-alpha-black-25 shadow-xs">
|
||||
<div class="space-y-4">
|
||||
<% 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 %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-white rounded-bl-lg rounded-br-lg">
|
||||
<%= render "pagination", pagy: @pagy, current_path: account_path(@account, page: params[:page], tab: params[:tab]) %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
|
@ -1,7 +1,7 @@
|
|||
<%# locals: (entry:, selectable: true, balance_trend: nil) %>
|
||||
<% transaction, account = entry.account_transaction, entry.account %>
|
||||
|
||||
<div class="grid grid-cols-12 items-center text-gray-900 text-sm font-medium p-4">
|
||||
<div class="grid grid-cols-12 items-center text-gray-900 text-sm font-medium p-4 <%= @focused_record == entry ? "border border-gray-900 rounded-lg" : "" %>">
|
||||
<div class="pr-10 flex items-center gap-4 <%= balance_trend ? "col-span-6" : "col-span-8" %>">
|
||||
<% 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_record_id: entry.id), data: { turbo_frame: "_top" }, class: "hover:underline" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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 %>
|
||||
<div class="bg-white p-5 border border-alpha-black-25 rounded-xl shadow-xs" data-controller="focus-record" data-focus-record-id-value="<%= @focused_record ? dom_id(@focused_record) : nil %>">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<%= tag.h2 t(".title"), class: "font-medium text-lg" %>
|
||||
<% unless @account.plaid_account_id.present? %>
|
||||
<div data-controller="menu" data-testid="activity-menu">
|
||||
<button class="btn btn--secondary flex items-center gap-2" data-menu-target="button">
|
||||
<%= lucide_icon("plus", class: "w-4 h-4") %>
|
||||
<%= tag.span t(".new") %>
|
||||
</button>
|
||||
<div data-menu-target="content" class="z-10 hidden bg-white rounded-lg border border-alpha-black-25 shadow-xs p-1">
|
||||
<%= link_to new_account_valuation_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %>
|
||||
<%= lucide_icon("circle-dollar-sign", class: "text-gray-500 w-5 h-5") %>
|
||||
<%= tag.span t(".new_balance"), class: "text-sm" %>
|
||||
<% end %>
|
||||
|
||||
<% unless @account.crypto? %>
|
||||
<%= link_to @account.investment? ? new_account_trade_path(account_id: @account.id) : new_account_transaction_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %>
|
||||
<%= lucide_icon("credit-card", class: "text-gray-500 w-5 h-5") %>
|
||||
<%= tag.span t(".new_transaction"), class: "text-sm" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= form_with url: account_path(account),
|
||||
id: "entries-search",
|
||||
scope: :q,
|
||||
method: :get,
|
||||
data: { controller: "auto-submit-form" } do |form| %>
|
||||
<div class="flex gap-2 mb-4">
|
||||
<div class="grow">
|
||||
<div class="flex items-center px-3 py-2 gap-2 border border-gray-200 rounded-lg focus-within:ring-gray-100 focus-within:border-gray-900">
|
||||
<%= 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" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @entries.empty? %>
|
||||
<p class="text-gray-500 text-sm p-4"><%= t(".no_entries") %></p>
|
||||
<% 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 %>
|
||||
<div id="entry-selection-bar" data-bulk-select-target="selectionBar" class="flex justify-center hidden">
|
||||
<%= render "account/entries/selection_bar" %>
|
||||
</div>
|
||||
|
||||
<div class="grid bg-gray-25 rounded-xl grid-cols-12 items-center uppercase text-xs font-medium text-gray-500 px-5 py-3 mb-4">
|
||||
<div class="pl-0.5 col-span-8 flex items-center gap-4">
|
||||
<%= check_box_tag "selection_entry",
|
||||
class: "maybe-checkbox maybe-checkbox--light",
|
||||
data: { action: "bulk-select#togglePageSelection" } %>
|
||||
<p><%= t(".date") %></p>
|
||||
</div>
|
||||
<%= tag.p t(".amount"), class: "col-span-2 justify-self-end" %>
|
||||
<%= tag.p t(".balance"), class: "col-span-2 justify-self-end" %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="rounded-tl-lg rounded-tr-lg bg-white border-alpha-black-25 shadow-xs">
|
||||
<div class="space-y-4">
|
||||
<% 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 %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-white rounded-bl-lg rounded-br-lg">
|
||||
<%= render "pagination", pagy: @pagy %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<%# locals: (pagy:, current_path: nil) %>
|
||||
<%# locals: (pagy:) %>
|
||||
|
||||
<nav class="flex w-full items-center justify-between">
|
||||
<div class="flex items-center gap-1">
|
||||
<div>
|
||||
<% if pagy.prev %>
|
||||
<%= link_to custom_pagy_url_for(pagy, pagy.prev, current_path: current_path),
|
||||
class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700",
|
||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
||||
<%= link_to pagy_url_for(pagy, pagy.prev),
|
||||
data: { turbo_frame: :_top },
|
||||
class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= lucide_icon("chevron-left", class: "w-5 h-5 text-gray-500") %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
|
@ -17,15 +18,15 @@
|
|||
<div class="rounded-xl p-1 bg-gray-25">
|
||||
<% pagy.series.each do |series_item| %>
|
||||
<% if series_item.is_a?(Integer) %>
|
||||
<%= link_to custom_pagy_url_for(pagy, series_item, current_path: current_path),
|
||||
class: "rounded-md px-2 py-1 inline-flex items-center text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700",
|
||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
||||
<%= link_to pagy_url_for(pagy, series_item),
|
||||
data: { turbo_frame: :_top },
|
||||
class: "rounded-md px-2 py-1 inline-flex items-center text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= series_item %>
|
||||
<% end %>
|
||||
<% elsif series_item.is_a?(String) %>
|
||||
<%= link_to custom_pagy_url_for(pagy, series_item, current_path: current_path),
|
||||
class: "rounded-md px-2 py-1 bg-white border border-alpha-black-25 shadow-xs inline-flex items-center text-sm font-medium text-gray-900",
|
||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
||||
<%= link_to pagy_url_for(pagy, series_item),
|
||||
data: { turbo_frame: :_top },
|
||||
class: "rounded-md px-2 py-1 bg-white border border-alpha-black-25 shadow-xs inline-flex items-center text-sm font-medium text-gray-900" do %>
|
||||
<%= series_item %>
|
||||
<% end %>
|
||||
<% elsif series_item == :gap %>
|
||||
|
@ -35,9 +36,9 @@
|
|||
</div>
|
||||
<div>
|
||||
<% if pagy.next %>
|
||||
<%= link_to custom_pagy_url_for(pagy, pagy.next, current_path: current_path),
|
||||
class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700",
|
||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
||||
<%= link_to pagy_url_for(pagy, pagy.next),
|
||||
data: { turbo_frame: :_top },
|
||||
class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-500") %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
|
@ -48,16 +49,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<%= form_with url: custom_pagy_url_for(pagy, pagy.page, current_path: current_path),
|
||||
method: :get,
|
||||
class: "flex items-center gap-4",
|
||||
data: { controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.label :per_page, t(".rows_per_page"), class: "text-sm text-gray-500" %>
|
||||
<%= f.select :per_page,
|
||||
options_for_select(["10", "20", "30", "50"], pagy.limit),
|
||||
{},
|
||||
class: "py-1.5 pr-8 text-sm text-gray-900 font-medium border border-gray-200 rounded-lg focus:border-gray-900 focus:ring-gray-900 focus-visible:ring-gray-900",
|
||||
data: { "auto-submit-form-target": "auto" } %>
|
||||
<% end %>
|
||||
<%= select_tag :per_page,
|
||||
options_for_select(["10", "20", "30", "50"], pagy.limit),
|
||||
data: { controller: "selectable-link" },
|
||||
class: "py-1.5 pr-8 text-sm text-gray-900 font-medium border border-gray-200 rounded-lg focus:border-gray-900 focus:ring-gray-900 focus-visible:ring-gray-900" %>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<%= render "accounts/show/template",
|
||||
account: @account,
|
||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) },
|
||||
{ key: "overview", contents: render("credit_cards/overview", account: @account) },
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) }
|
||||
]) %>
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
<div class="min-h-[800px]">
|
||||
<%= render "accounts/show/tabs", account: @account, tabs: [
|
||||
{ key: "holdings", contents: render("investments/holdings_tab", account: @account) },
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) },
|
||||
{ key: "holdings", contents: render("investments/holdings_tab", account: @account) },
|
||||
] %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<%= render "accounts/show/template",
|
||||
account: @account,
|
||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) },
|
||||
{ key: "overview", contents: render("loans/overview", account: @account) },
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) }
|
||||
]) %>
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
account: @account,
|
||||
header: render("accounts/show/header", account: @account, subtitle: @account.property.address),
|
||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) },
|
||||
{ key: "overview", contents: render("properties/overview", account: @account) },
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) }
|
||||
]) %>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="space-y-4 h-full flex flex-col overflow-y-auto">
|
||||
<div class="space-y-4 h-full flex flex-col overflow-y-auto" data-controller="focus-record" data-focus-record-id-value="<%= @focused_record ? dom_id(@focused_record) : nil %>">
|
||||
<%= render "header" %>
|
||||
|
||||
<%= render "summary", totals: @totals %>
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
scope: :q,
|
||||
method: :get,
|
||||
data: { controller: "auto-submit-form" } do |form| %>
|
||||
<%= hidden_field_tag :per_page, params[:per_page] %>
|
||||
|
||||
<div class="flex gap-2 mb-4">
|
||||
<div class="grow">
|
||||
<div class="flex items-center px-3 py-2 gap-2 border border-gray-200 rounded-lg focus-within:ring-gray-100 focus-within:border-gray-900">
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<div class="flex justify-between items-center gap-2 bg-white p-3">
|
||||
<div>
|
||||
<% if @q.present? %>
|
||||
<%= link_to t(".clear_filters"), transactions_path, class: "btn btn--ghost" %>
|
||||
<%= link_to t(".clear_filters"), transactions_path(clear_filters: true), class: "btn btn--ghost" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
<% 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 %>
|
||||
</li>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<%= render "accounts/show/template",
|
||||
account: @account,
|
||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) },
|
||||
{ key: "overview", contents: render("vehicles/overview", account: @account) },
|
||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) }
|
||||
]) %>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue