Skip to content
This repository was archived by the owner on Jul 27, 2025. It is now read-only.

Commit 8c97c9d

Browse files
authored
Consolidate and simplify account pages (#2462)
* Remove ScrollFocusable * Consolidate and simplify account pages * Lint fixes * Fix tab param initialization * Remove stale files * Remove stale route, make accountable routes clearer
1 parent 3eea5a9 commit 8c97c9d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+252
-269
lines changed
Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,28 @@
1-
<%# locals: (account:, tooltip: nil, chart_view: nil, **args) %>
2-
3-
<% period = @period || Period.last_30_days %>
4-
<% default_value_title = account.asset? ? t(".balance") : t(".owed") %>
5-
61
<div id="<%= dom_id(account, :chart) %>" class="bg-container shadow-border-xs rounded-xl space-y-2">
72
<div class="flex justify-between flex-col-reverse lg:flex-row gap-2 px-4 pt-4 mb-2">
83
<div class="space-y-2 w-full">
94
<div class="flex items-center gap-1">
10-
<%= tag.p account.investment? ? "Total value" : default_value_title, class: "text-sm font-medium text-secondary" %>
5+
<%= tag.p title, class: "text-sm font-medium text-secondary" %>
116

127
<% if account.investment? %>
13-
<%= render "investments/value_tooltip", balance: account.balance_money, holdings: account.balance_money - account.cash_balance_money, cash: account.cash_balance_money %>
8+
<%= render "investments/value_tooltip", balance: account.balance_money, holdings: holdings_value_money, cash: account.cash_balance_money %>
149
<% end %>
1510
</div>
1611
<div class="flex flex-row gap-2 items-baseline">
17-
<%= tag.p format_money(account.balance_money), class: "text-primary text-3xl font-medium truncate" %>
18-
<% if account.currency != Current.family.currency %>
19-
<%= tag.p format_money(account.balance_money.exchange_to(Current.family.currency, fallback_rate: 1)), class: "text-sm font-medium text-secondary" %>
12+
<%= tag.p view_balance_money.format, class: "text-primary text-3xl font-medium truncate" %>
13+
14+
<% if converted_balance_money %>
15+
<%= tag.p converted_balance_money.format, class: "text-sm font-medium text-secondary" %>
2016
<% end %>
2117
</div>
2218
</div>
2319

24-
<%= form_with url: request.path, method: :get, data: { controller: "auto-submit-form" } do |form| %>
20+
<%= form_with url: account_path(account), method: :get, data: { controller: "auto-submit-form" } do |form| %>
2521
<div class="flex items-center gap-2">
26-
<% if chart_view.present? %>
22+
<% if account.investment? %>
2723
<%= form.select :chart_view,
2824
[["Total value", "balance"], ["Holdings", "holdings_balance"], ["Cash", "cash_balance"]],
29-
{ selected: chart_view },
25+
{ selected: view },
3026
class: "bg-container border border-secondary rounded-lg text-sm pr-7 cursor-pointer text-primary focus:outline-hidden focus:ring-0",
3127
data: { "auto-submit-form-target": "auto" } %>
3228
<% end %>
@@ -40,7 +36,23 @@
4036
<% end %>
4137
</div>
4238

43-
<%= turbo_frame_tag dom_id(account, :chart_details), src: chart_account_path(account, period: period.key, chart_view: chart_view) do %>
44-
<%= render "accounts/chart_loader" %>
39+
<%= turbo_frame_tag dom_id(@account, :chart_details) do %>
40+
<div class="px-4">
41+
<%= render partial: "shared/trend_change", locals: { trend: trend, comparison_label: period.comparison_label } %>
42+
</div>
43+
44+
<div class="h-64 pb-4">
45+
<% if series.any? %>
46+
<div
47+
id="lineChart"
48+
class="w-full h-full"
49+
data-controller="time-series-chart"
50+
data-time-series-chart-data-value="<%= series.to_json %>"></div>
51+
<% else %>
52+
<div class="w-full h-full flex items-center justify-center">
53+
<p class="text-secondary text-sm">No data available</p>
54+
</div>
55+
<% end %>
56+
</div>
4557
<% end %>
4658
</div>

app/components/UI/account/chart.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
class UI::Account::Chart < ApplicationComponent
2+
attr_reader :account
3+
4+
def initialize(account:, period: nil, view: nil)
5+
@account = account
6+
@period = period
7+
@view = view
8+
end
9+
10+
def period
11+
@period ||= Period.last_30_days
12+
end
13+
14+
def holdings_value_money
15+
account.balance_money - account.cash_balance_money
16+
end
17+
18+
def view_balance_money
19+
case view
20+
when "balance"
21+
account.balance_money
22+
when "holdings_balance"
23+
holdings_value_money
24+
when "cash_balance"
25+
account.cash_balance_money
26+
end
27+
end
28+
29+
def title
30+
case account.accountable_type
31+
when "Investment", "Crypto"
32+
case view
33+
when "balance"
34+
"Total account value"
35+
when "holdings_balance"
36+
"Holdings value"
37+
when "cash_balance"
38+
"Cash value"
39+
end
40+
when "Property", "Vehicle"
41+
"Estimated #{account.accountable_type.humanize.downcase} value"
42+
when "CreditCard", "OtherLiability"
43+
"Debt balance"
44+
when "Loan"
45+
"Remaining principal balance"
46+
else
47+
"Balance"
48+
end
49+
end
50+
51+
def foreign_currency?
52+
account.currency != account.family.currency
53+
end
54+
55+
def converted_balance_money
56+
return nil unless foreign_currency?
57+
58+
account.balance_money.exchange_to(account.family.currency, fallback_rate: 1)
59+
end
60+
61+
def view
62+
@view ||= "balance"
63+
end
64+
65+
def series
66+
account.balance_series(period: period, view: view)
67+
end
68+
69+
def trend
70+
series.trend
71+
end
72+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<%= turbo_stream_from account %>
2+
3+
<%= turbo_frame_tag dom_id(account, :container) do %>
4+
<%= tag.div class: "space-y-4 pb-32" do %>
5+
<%= render "accounts/show/header", account: account, title: title, subtitle: subtitle %>
6+
7+
<%= render UI::Account::Chart.new(account: account, period: chart_period, view: chart_view) %>
8+
9+
<div class="min-h-[800px]" data-testid="account-details">
10+
<% if tabs.count > 1 %>
11+
<%= render TabsComponent.new(active_tab: active_tab, url_param_key: "tab") do |tabs_container| %>
12+
<% tabs_container.with_nav(classes: "max-w-fit") do |nav| %>
13+
<% tabs.each do |tab| %>
14+
<% nav.with_btn(id: tab, label: tab.to_s.humanize, classes: "px-6") %>
15+
<% end %>
16+
<% end %>
17+
18+
<% tabs.each do |tab| %>
19+
<% tabs_container.with_panel(tab_id: tab) do %>
20+
<%= render tab_partial_name(tab), account: account %>
21+
<% end %>
22+
<% end %>
23+
<% end %>
24+
<% else %>
25+
<%= render tab_partial_name(tabs.first), account: account %>
26+
<% end %>
27+
</div>
28+
<% end %>
29+
<% end %>

app/components/UI/account_page.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class UI::AccountPage < ApplicationComponent
2+
attr_reader :account, :chart_view, :chart_period
3+
4+
def initialize(account:, chart_view: nil, chart_period: nil, active_tab: nil)
5+
@account = account
6+
@chart_view = chart_view
7+
@chart_period = chart_period
8+
@active_tab = active_tab
9+
end
10+
11+
def title
12+
account.name
13+
end
14+
15+
def subtitle
16+
return nil unless account.property?
17+
18+
account.property.address
19+
end
20+
21+
def active_tab
22+
tabs.find { |tab| tab == @active_tab&.to_sym } || tabs.first
23+
end
24+
25+
def tabs
26+
case account.accountable_type
27+
when "Investment"
28+
[ :activity, :holdings ]
29+
when "Property", "Vehicle", "Loan"
30+
[ :activity, :overview ]
31+
else
32+
[ :activity ]
33+
end
34+
end
35+
36+
def tab_partial_name(tab)
37+
case tab
38+
when :activity
39+
"accounts/show/activity"
40+
when :holdings, :overview
41+
# Accountable is responsible for implementing the partial in the correct folder
42+
"#{account.accountable_type.downcase.pluralize}/tabs/#{tab}"
43+
end
44+
end
45+
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class ApplicationComponent < ViewComponent::Base
2+
# These don't work as expected with helpers.turbo_frame_tag, etc., so we include them here
3+
include Turbo::FramesHelper, Turbo::StreamsHelper
4+
end

app/controllers/accounts_controller.rb

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class AccountsController < ApplicationController
2-
before_action :set_account, only: %i[sync chart sparkline toggle_active]
2+
before_action :set_account, only: %i[sync sparkline toggle_active show destroy]
33
include Periodable
44

55
def index
@@ -9,6 +9,15 @@ def index
99
render layout: "settings"
1010
end
1111

12+
def show
13+
@chart_view = params[:chart_view] || "balance"
14+
@tab = params[:tab]
15+
@q = params.fetch(:q, {}).permit(:search)
16+
entries = @account.entries.search(@q).reverse_chronological
17+
18+
@pagy, @entries = pagy(entries, limit: params[:per_page] || "10")
19+
end
20+
1221
def sync
1322
unless @account.syncing?
1423
@account.sync_later
@@ -17,11 +26,6 @@ def sync
1726
redirect_to account_path(@account)
1827
end
1928

20-
def chart
21-
@chart_view = params[:chart_view] || "balance"
22-
render layout: "application"
23-
end
24-
2529
def sparkline
2630
etag_key = @account.family.build_cache_key("#{@account.id}_sparkline", invalidate_on_data_updates: true)
2731

@@ -42,6 +46,15 @@ def toggle_active
4246
redirect_to accounts_path
4347
end
4448

49+
def destroy
50+
if @account.linked?
51+
redirect_to account_path(@account), alert: "Cannot delete a linked account"
52+
else
53+
@account.destroy_later
54+
redirect_to accounts_path, notice: "Account scheduled for deletion"
55+
end
56+
end
57+
4558
private
4659
def family
4760
Current.family

app/controllers/concerns/accountable_resource.rb

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ module AccountableResource
22
extend ActiveSupport::Concern
33

44
included do
5-
include ScrollFocusable, Periodable
5+
include Periodable
66

7-
before_action :set_account, only: [ :show, :edit, :update, :destroy ]
7+
before_action :set_account, only: [ :show, :edit, :update ]
88
before_action :set_link_options, only: :new
99
end
1010

@@ -27,9 +27,7 @@ def show
2727
@q = params.fetch(:q, {}).permit(:search)
2828
entries = @account.entries.search(@q).reverse_chronological
2929

30-
set_focused_record(entries, params[:focused_record_id])
31-
32-
@pagy, @entries = pagy(entries, limit: params[:per_page] || "10", params: ->(params) { params.except(:focused_record_id) })
30+
@pagy, @entries = pagy(entries, limit: params[:per_page] || "10")
3331
end
3432

3533
def edit
@@ -63,16 +61,7 @@ def update
6361
end
6462

6563
@account.lock_saved_attributes!
66-
redirect_back_or_to @account, notice: t("accounts.update.success", type: accountable_type.name.underscore.humanize)
67-
end
68-
69-
def destroy
70-
if @account.linked?
71-
redirect_to account_path(@account), alert: "Cannot delete a linked account"
72-
else
73-
@account.destroy_later
74-
redirect_to accounts_path, notice: t("accounts.destroy.success", type: accountable_type.name.underscore.humanize)
75-
end
64+
redirect_back_or_to account_path(@account), notice: t("accounts.update.success", type: accountable_type.name.underscore.humanize)
7665
end
7766

7867
private

app/controllers/concerns/scroll_focusable.rb

Lines changed: 0 additions & 21 deletions
This file was deleted.

app/controllers/transactions_controller.rb

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class TransactionsController < ApplicationController
2-
include ScrollFocusable, EntryableResource
2+
include EntryableResource
33

44
before_action :store_params!, only: :index
55

@@ -21,12 +21,7 @@ def index
2121
:transfer_as_inflow, :transfer_as_outflow
2222
)
2323

24-
@pagy, @transactions = pagy(base_scope, limit: per_page, params: ->(p) { p.except(:focused_record_id) })
25-
26-
# No performance penalty by default. Only runs queries if the record is set.
27-
if params[:focused_record_id].present?
28-
set_focused_record(base_scope, params[:focused_record_id], default_per_page: per_page)
29-
end
24+
@pagy, @transactions = pagy(base_scope, limit: per_page)
3025
end
3126

3227
def clear_filter

app/javascript/controllers/focus_record_controller.js

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)