mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-19 21:29:38 +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
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
|
include ScrollFocusable
|
||||||
|
|
||||||
layout :with_sidebar
|
layout :with_sidebar
|
||||||
before_action :set_account, only: [ :show, :edit, :update, :destroy ]
|
before_action :set_account, only: [ :show, :edit, :update, :destroy ]
|
||||||
before_action :set_link_token, only: :new
|
before_action :set_link_token, only: :new
|
||||||
|
@ -22,6 +24,12 @@ module AccountableResource
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
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
|
end
|
||||||
|
|
||||||
def edit
|
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
|
class TransactionsController < ApplicationController
|
||||||
|
include ScrollFocusable
|
||||||
|
|
||||||
layout :with_sidebar
|
layout :with_sidebar
|
||||||
|
|
||||||
|
before_action :store_params!, only: :index
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@q = search_params
|
@q = search_params
|
||||||
search_query = Current.family.transactions.search(@q).reverse_chronological
|
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
|
totals_query = search_query.incomes_and_expenses
|
||||||
family_currency = Current.family.currency
|
family_currency = Current.family.currency
|
||||||
|
@ -18,13 +29,76 @@ class TransactionsController < ApplicationController
|
||||||
}
|
}
|
||||||
end
|
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
|
private
|
||||||
def search_params
|
def search_params
|
||||||
params.fetch(:q, {})
|
cleaned_params = params.fetch(:q, {})
|
||||||
.permit(
|
.permit(
|
||||||
:start_date, :end_date, :search, :amount,
|
:start_date, :end_date, :search, :amount,
|
||||||
:amount_operator, accounts: [], account_ids: [],
|
:amount_operator, accounts: [], account_ids: [],
|
||||||
categories: [], merchants: [], types: [], tags: []
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -176,24 +176,4 @@ module ApplicationHelper
|
||||||
|
|
||||||
cookies[:admin] == "true"
|
cookies[:admin] == "true"
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -18,21 +18,4 @@ module TransactionsHelper
|
||||||
def get_default_transaction_search_filter
|
def get_default_transaction_search_filter
|
||||||
transaction_search_filters[0]
|
transaction_search_filters[0]
|
||||||
end
|
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
|
||||||
|
|
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) %>
|
<%# locals: (entry:, selectable: true, balance_trend: nil) %>
|
||||||
<% transaction, account = entry.account_transaction, entry.account %>
|
<% 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" %>">
|
<div class="pr-10 flex items-center gap-4 <%= balance_trend ? "col-span-6" : "col-span-8" %>">
|
||||||
<% if selectable %>
|
<% if selectable %>
|
||||||
<%= check_box_tag dom_id(entry, "selection"),
|
<%= check_box_tag dom_id(entry, "selection"),
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
<% if entry.account_transaction.transfer? %>
|
<% if entry.account_transaction.transfer? %>
|
||||||
<%= render "transfers/account_links", transfer: entry.account_transaction.transfer, is_inflow: entry.account_transaction.transfer_as_inflow.present? %>
|
<%= render "transfers/account_links", transfer: entry.account_transaction.transfer, is_inflow: entry.account_transaction.transfer_as_inflow.present? %>
|
||||||
<% else %>
|
<% 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 %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,95 @@
|
||||||
<%# locals: (account:) %>
|
<%# locals: (account:) %>
|
||||||
|
|
||||||
<%= turbo_frame_tag dom_id(account, :entries), src: account_entries_path(account_id: account.id, page: params[:page], tab: params[:tab]) do %>
|
<%= turbo_frame_tag dom_id(account, "entries") do %>
|
||||||
<%= render "account/entries/loading" %>
|
<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 %>
|
<% end %>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<%# locals: (pagy:, current_path: nil) %>
|
<%# locals: (pagy:) %>
|
||||||
|
|
||||||
<nav class="flex w-full items-center justify-between">
|
<nav class="flex w-full items-center justify-between">
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<div>
|
<div>
|
||||||
<% if pagy.prev %>
|
<% if pagy.prev %>
|
||||||
<%= link_to custom_pagy_url_for(pagy, pagy.prev, current_path: current_path),
|
<%= link_to pagy_url_for(pagy, pagy.prev),
|
||||||
class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700",
|
data: { turbo_frame: :_top },
|
||||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
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") %>
|
<%= lucide_icon("chevron-left", class: "w-5 h-5 text-gray-500") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
|
@ -17,15 +18,15 @@
|
||||||
<div class="rounded-xl p-1 bg-gray-25">
|
<div class="rounded-xl p-1 bg-gray-25">
|
||||||
<% pagy.series.each do |series_item| %>
|
<% pagy.series.each do |series_item| %>
|
||||||
<% if series_item.is_a?(Integer) %>
|
<% if series_item.is_a?(Integer) %>
|
||||||
<%= link_to custom_pagy_url_for(pagy, series_item, current_path: current_path),
|
<%= link_to pagy_url_for(pagy, series_item),
|
||||||
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: { turbo_frame: :_top },
|
||||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
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 %>
|
<%= series_item %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% elsif series_item.is_a?(String) %>
|
<% elsif series_item.is_a?(String) %>
|
||||||
<%= link_to custom_pagy_url_for(pagy, series_item, current_path: current_path),
|
<%= link_to pagy_url_for(pagy, series_item),
|
||||||
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: { turbo_frame: :_top },
|
||||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
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 %>
|
<%= series_item %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% elsif series_item == :gap %>
|
<% elsif series_item == :gap %>
|
||||||
|
@ -35,9 +36,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<% if pagy.next %>
|
<% if pagy.next %>
|
||||||
<%= link_to custom_pagy_url_for(pagy, pagy.next, current_path: current_path),
|
<%= link_to pagy_url_for(pagy, pagy.next),
|
||||||
class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700",
|
data: { turbo_frame: :_top },
|
||||||
data: (current_path ? { turbo_frame: "_top" } : {}) do %>
|
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") %>
|
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-500") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
|
@ -48,16 +49,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<%= form_with url: custom_pagy_url_for(pagy, pagy.page, current_path: current_path),
|
<%= select_tag :per_page,
|
||||||
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),
|
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",
|
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 %>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<%= render "accounts/show/template",
|
<%= render "accounts/show/template",
|
||||||
account: @account,
|
account: @account,
|
||||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
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: "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]">
|
<div class="min-h-[800px]">
|
||||||
<%= render "accounts/show/tabs", account: @account, tabs: [
|
<%= 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: "activity", contents: render("accounts/show/activity", account: @account) },
|
||||||
|
{ key: "holdings", contents: render("investments/holdings_tab", account: @account) },
|
||||||
] %>
|
] %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<%= render "accounts/show/template",
|
<%= render "accounts/show/template",
|
||||||
account: @account,
|
account: @account,
|
||||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
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: "overview", contents: render("loans/overview", account: @account) },
|
||||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) }
|
|
||||||
]) %>
|
]) %>
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
account: @account,
|
account: @account,
|
||||||
header: render("accounts/show/header", account: @account, subtitle: @account.property.address),
|
header: render("accounts/show/header", account: @account, subtitle: @account.property.address),
|
||||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
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: "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 "header" %>
|
||||||
|
|
||||||
<%= render "summary", totals: @totals %>
|
<%= render "summary", totals: @totals %>
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
scope: :q,
|
scope: :q,
|
||||||
method: :get,
|
method: :get,
|
||||||
data: { controller: "auto-submit-form" } do |form| %>
|
data: { controller: "auto-submit-form" } do |form| %>
|
||||||
|
<%= hidden_field_tag :per_page, params[:per_page] %>
|
||||||
|
|
||||||
<div class="flex gap-2 mb-4">
|
<div class="flex gap-2 mb-4">
|
||||||
<div class="grow">
|
<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">
|
<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 class="flex justify-between items-center gap-2 bg-white p-3">
|
||||||
<div>
|
<div>
|
||||||
<% if @q.present? %>
|
<% 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 %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% 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" %>
|
<%= lucide_icon "x", class: "w-4 h-4 text-gray-500" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<%= render "accounts/show/template",
|
<%= render "accounts/show/template",
|
||||||
account: @account,
|
account: @account,
|
||||||
tabs: render("accounts/show/tabs", account: @account, tabs: [
|
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: "overview", contents: render("vehicles/overview", account: @account) },
|
||||||
{ key: "activity", contents: render("accounts/show/activity", account: @account) }
|
|
||||||
]) %>
|
]) %>
|
||||||
|
|
|
@ -6,16 +6,17 @@ en:
|
||||||
transfer:
|
transfer:
|
||||||
attributes:
|
attributes:
|
||||||
base:
|
base:
|
||||||
|
inflow_cannot_be_in_multiple_transfers: Inflow transaction cannot be
|
||||||
|
part of multiple transfers
|
||||||
must_be_from_different_accounts: Transfer must have different accounts
|
must_be_from_different_accounts: Transfer must have different accounts
|
||||||
|
must_be_from_same_family: Transfer must be from the same family
|
||||||
must_be_within_date_range: Transfer transaction dates must be within
|
must_be_within_date_range: Transfer transaction dates must be within
|
||||||
4 days of each other
|
4 days of each other
|
||||||
must_have_opposite_amounts: Transfer transactions must have opposite
|
must_have_opposite_amounts: Transfer transactions must have opposite
|
||||||
amounts
|
amounts
|
||||||
must_have_single_currency: Transfer must have a single currency
|
must_have_single_currency: Transfer must have a single currency
|
||||||
must_be_from_same_family: Transfer must be from the same family
|
outflow_cannot_be_in_multiple_transfers: Outflow transaction cannot
|
||||||
inflow_cannot_be_in_multiple_transfers: Inflow transaction cannot be part of multiple transfers
|
be part of multiple transfers
|
||||||
outflow_cannot_be_in_multiple_transfers: Outflow transaction cannot be part of multiple transfers
|
|
||||||
transfer:
|
transfer:
|
||||||
name: Transfer to %{to_account}
|
name: Transfer to %{to_account}
|
||||||
payment_name: Payment to %{to_account}
|
payment_name: Payment to %{to_account}
|
||||||
|
|
||||||
|
|
|
@ -9,17 +9,6 @@ en:
|
||||||
empty:
|
empty:
|
||||||
description: Try adding an entry, editing filters or refining your search
|
description: Try adding an entry, editing filters or refining your search
|
||||||
title: No entries found
|
title: No entries found
|
||||||
index:
|
|
||||||
amount: Amount
|
|
||||||
balance: Balance
|
|
||||||
date: Date
|
|
||||||
entries: entries
|
|
||||||
entry: entry
|
|
||||||
new: New
|
|
||||||
new_balance: New balance
|
|
||||||
new_transaction: New transaction
|
|
||||||
no_entries: No entries found
|
|
||||||
title: Activity
|
|
||||||
loading:
|
loading:
|
||||||
loading: Loading entries...
|
loading: Loading entries...
|
||||||
update:
|
update:
|
||||||
|
|
|
@ -34,6 +34,17 @@ en:
|
||||||
title: How would you like to add it?
|
title: How would you like to add it?
|
||||||
title: What would you like to add?
|
title: What would you like to add?
|
||||||
show:
|
show:
|
||||||
|
activity:
|
||||||
|
amount: Amount
|
||||||
|
balance: Balance
|
||||||
|
date: Date
|
||||||
|
entries: entries
|
||||||
|
entry: entry
|
||||||
|
new: New
|
||||||
|
new_balance: New balance
|
||||||
|
new_transaction: New transaction
|
||||||
|
no_entries: No entries found
|
||||||
|
title: Activity
|
||||||
chart:
|
chart:
|
||||||
balance: Balance
|
balance: Balance
|
||||||
owed: Amount owed
|
owed: Amount owed
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
---
|
---
|
||||||
en:
|
en:
|
||||||
application:
|
|
||||||
pagination:
|
|
||||||
rows_per_page: Rows per page
|
|
||||||
number:
|
number:
|
||||||
currency:
|
currency:
|
||||||
format:
|
format:
|
||||||
|
|
|
@ -15,10 +15,10 @@ en:
|
||||||
form:
|
form:
|
||||||
placeholder: Category name
|
placeholder: Category name
|
||||||
index:
|
index:
|
||||||
categories: Categories
|
|
||||||
bootstrap: Use default categories
|
bootstrap: Use default categories
|
||||||
categories_incomes: Income categories
|
categories: Categories
|
||||||
categories_expenses: Expense categories
|
categories_expenses: Expense categories
|
||||||
|
categories_incomes: Income categories
|
||||||
empty: No categories found
|
empty: No categories found
|
||||||
new: New category
|
new: New category
|
||||||
menu:
|
menu:
|
||||||
|
|
|
@ -15,8 +15,8 @@ en:
|
||||||
description: Issue Description
|
description: Issue Description
|
||||||
sidebar:
|
sidebar:
|
||||||
accounts: Accounts
|
accounts: Accounts
|
||||||
|
budgeting: Budgeting
|
||||||
dashboard: Dashboard
|
dashboard: Dashboard
|
||||||
new_account: New account
|
new_account: New account
|
||||||
portfolio: Portfolio
|
portfolio: Portfolio
|
||||||
transactions: Transactions
|
transactions: Transactions
|
||||||
budgeting: Budgeting
|
|
||||||
|
|
|
@ -82,8 +82,6 @@ Rails.application.routes.draw do
|
||||||
namespace :account do
|
namespace :account do
|
||||||
resources :holdings, only: %i[index new show destroy]
|
resources :holdings, only: %i[index new show destroy]
|
||||||
|
|
||||||
resources :entries, only: :index
|
|
||||||
|
|
||||||
resources :transactions, only: %i[show new create update destroy] do
|
resources :transactions, only: %i[show new create update destroy] do
|
||||||
resource :transfer_match, only: %i[new create]
|
resource :transfer_match, only: %i[new create]
|
||||||
resource :category, only: :update, controller: :transaction_categories
|
resource :category, only: :update, controller: :transaction_categories
|
||||||
|
@ -109,7 +107,11 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :transactions, only: :index
|
resources :transactions, only: :index do
|
||||||
|
collection do
|
||||||
|
delete :clear_filter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Convenience routes for polymorphic paths
|
# Convenience routes for polymorphic paths
|
||||||
# Example: account_path(Account.new(accountable: Depository.new)) => /depositories/123
|
# Example: account_path(Account.new(accountable: Depository.new)) => /depositories/123
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class StoreTransactionFiltersInSession < ActiveRecord::Migration[7.2]
|
||||||
|
def change
|
||||||
|
add_column :sessions, :prev_transaction_page_params, :jsonb, default: {}
|
||||||
|
end
|
||||||
|
end
|
3
db/schema.rb
generated
3
db/schema.rb
generated
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
enable_extension "plpgsql"
|
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.datetime "updated_at", null: false
|
||||||
t.uuid "active_impersonator_session_id"
|
t.uuid "active_impersonator_session_id"
|
||||||
t.datetime "subscribed_at"
|
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 ["active_impersonator_session_id"], name: "index_sessions_on_active_impersonator_session_id"
|
||||||
t.index ["user_id"], name: "index_sessions_on_user_id"
|
t.index ["user_id"], name: "index_sessions_on_user_id"
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
||||||
Capybara.default_max_wait_time = 5
|
Capybara.default_max_wait_time = 5
|
||||||
end
|
end
|
||||||
|
|
||||||
driven_by :selenium, using: ENV["CI"].present? ? :headless_chrome : :chrome, screen_size: [ 1400, 1400 ]
|
driven_by :selenium, using: ENV["CI"].present? ? :headless_chrome : ENV.fetch("E2E_BROWSER", :chrome).to_sym, screen_size: [ 1400, 1400 ]
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class Account::EntriesControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
setup do
|
|
||||||
sign_in @user = users(:family_admin)
|
|
||||||
@entry = account_entries(:transaction)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "gets index" do
|
|
||||||
get account_entries_path(account_id: @entry.account.id)
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -79,7 +79,7 @@ class TradesTest < ApplicationSystemTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_account_portfolio
|
def visit_account_portfolio
|
||||||
visit account_path(@account)
|
visit account_path(@account, tab: "holdings")
|
||||||
end
|
end
|
||||||
|
|
||||||
def select_combobox_option(text)
|
def select_combobox_option(text)
|
||||||
|
|
|
@ -6,7 +6,7 @@ class TransactionsTest < ApplicationSystemTestCase
|
||||||
|
|
||||||
Account::Entry.delete_all # clean slate
|
Account::Entry.delete_all # clean slate
|
||||||
|
|
||||||
@uncategorized_transaction = create_transaction("one", 12.days.ago.to_date, 100)
|
create_transaction("one", 12.days.ago.to_date, 100)
|
||||||
create_transaction("two", 10.days.ago.to_date, 100)
|
create_transaction("two", 10.days.ago.to_date, 100)
|
||||||
create_transaction("three", 9.days.ago.to_date, 100)
|
create_transaction("three", 9.days.ago.to_date, 100)
|
||||||
create_transaction("four", 8.days.ago.to_date, 100)
|
create_transaction("four", 8.days.ago.to_date, 100)
|
||||||
|
@ -15,7 +15,7 @@ class TransactionsTest < ApplicationSystemTestCase
|
||||||
create_transaction("seven", 4.days.ago.to_date, 100)
|
create_transaction("seven", 4.days.ago.to_date, 100)
|
||||||
create_transaction("eight", 3.days.ago.to_date, 100)
|
create_transaction("eight", 3.days.ago.to_date, 100)
|
||||||
create_transaction("nine", 1.days.ago.to_date, 100)
|
create_transaction("nine", 1.days.ago.to_date, 100)
|
||||||
create_transaction("ten", 1.days.ago.to_date, 100)
|
@uncategorized_transaction = create_transaction("ten", 1.days.ago.to_date, 100)
|
||||||
create_transaction("eleven", Date.current, 100, category: categories(:food_and_drink), tags: [ tags(:one) ], merchant: merchants(:amazon))
|
create_transaction("eleven", Date.current, 100, category: categories(:food_and_drink), tags: [ tags(:one) ], merchant: merchants(:amazon))
|
||||||
|
|
||||||
@transactions = @user.family.entries
|
@transactions = @user.family.entries
|
||||||
|
@ -124,13 +124,13 @@ class TransactionsTest < ApplicationSystemTestCase
|
||||||
assert_text "No entries found"
|
assert_text "No entries found"
|
||||||
|
|
||||||
within "ul#transaction-search-filters" do
|
within "ul#transaction-search-filters" do
|
||||||
find("li", text: account.name).first("a").click
|
find("li", text: account.name).first("button").click
|
||||||
find("li", text: "on or after #{10.days.ago.to_date}").first("a").click
|
find("li", text: "on or after #{10.days.ago.to_date}").first("button").click
|
||||||
find("li", text: "on or before #{1.day.ago.to_date}").first("a").click
|
find("li", text: "on or before #{1.day.ago.to_date}").first("button").click
|
||||||
find("li", text: "Income").first("a").click
|
find("li", text: "Income").first("button").click
|
||||||
find("li", text: "less than 200").first("a").click
|
find("li", text: "less than 200").first("button").click
|
||||||
find("li", text: category.name).first("a").click
|
find("li", text: category.name).first("button").click
|
||||||
find("li", text: merchant.name).first("a").click
|
find("li", text: merchant.name).first("button").click
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_selector "#" + dom_id(@transaction), count: 1
|
assert_selector "#" + dom_id(@transaction), count: 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue