From 246343a4bfdfb09f633e293e435d32d5818558c7 Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Wed, 14 May 2025 21:04:20 -0400 Subject: [PATCH] Sync completion event broadcasts --- .../maybe-design-system/border-utils.css | 5 - app/components/tabs_component.html.erb | 5 - app/components/tabs_controller.js | 18 +++- app/controllers/application_controller.rb | 4 +- ...pable.rb => restore_layout_preferences.rb} | 10 +- .../current_sessions_controller.rb | 14 +++ app/models/account/syncer.rb | 2 +- app/models/balance_sheet.rb | 7 +- app/models/family.rb | 8 -- app/models/plaid_item/syncer.rb | 2 +- app/models/session.rb | 10 ++ app/models/sync.rb | 16 +-- app/models/sync_complete_event.rb | 37 +++++++ .../accounts/_account_sidebar_tabs.html.erb | 10 +- .../accounts/_accountable_group.html.erb | 101 +++++++++--------- app/views/accounts/show/_chart.html.erb | 8 +- app/views/accounts/show/_template.html.erb | 4 +- app/views/investments/show.html.erb | 32 ++---- app/views/layouts/application.html.erb | 5 +- .../pages/dashboard/_balance_sheet.html.erb | 4 +- .../pages/dashboard/_net_worth_chart.html.erb | 79 +++++++------- config/routes.rb | 2 +- .../20250514214242_add_metadata_to_session.rb | 5 + db/schema.rb | 3 +- .../current_sessions_controller_test.rb | 15 +++ 25 files changed, 237 insertions(+), 169 deletions(-) rename app/controllers/concerns/{account_groupable.rb => restore_layout_preferences.rb} (58%) create mode 100644 app/controllers/current_sessions_controller.rb create mode 100644 app/models/sync_complete_event.rb create mode 100644 db/migrate/20250514214242_add_metadata_to_session.rb create mode 100644 test/controllers/current_sessions_controller_test.rb diff --git a/app/assets/tailwind/maybe-design-system/border-utils.css b/app/assets/tailwind/maybe-design-system/border-utils.css index bc2d7a60..94c54a55 100644 --- a/app/assets/tailwind/maybe-design-system/border-utils.css +++ b/app/assets/tailwind/maybe-design-system/border-utils.css @@ -1,7 +1,6 @@ /* Custom shadow borders used for surfaces / containers */ @utility shadow-border-xs { box-shadow: var(--shadow-xs), 0px 0px 0px 1px var(--color-alpha-black-50); - transform: translateZ(0); @variant theme-dark { box-shadow: var(--shadow-xs), 0px 0px 0px 1px var(--color-alpha-white-50); @@ -10,7 +9,6 @@ @utility shadow-border-sm { box-shadow: var(--shadow-sm), 0px 0px 0px 1px var(--color-alpha-black-50); - transform: translateZ(0); @variant theme-dark { box-shadow: var(--shadow-sm), 0px 0px 0px 1px var(--color-alpha-white-50); @@ -19,7 +17,6 @@ @utility shadow-border-md { box-shadow: var(--shadow-md), 0px 0px 0px 1px var(--color-alpha-black-50); - transform: translateZ(0); @variant theme-dark { box-shadow: var(--shadow-md), 0px 0px 0px 1px var(--color-alpha-white-50); @@ -28,7 +25,6 @@ @utility shadow-border-lg { box-shadow: var(--shadow-lg), 0px 0px 0px 1px var(--color-alpha-black-50); - transform: translateZ(0); @variant theme-dark { box-shadow: var(--shadow-lg), 0px 0px 0px 1px var(--color-alpha-white-50); @@ -37,7 +33,6 @@ @utility shadow-border-xl { box-shadow: var(--shadow-xl), 0px 0px 0px 1px var(--color-alpha-black-50); - transform: translateZ(0); @variant theme-dark { box-shadow: var(--shadow-xl), 0px 0px 0px 1px var(--color-alpha-white-50); diff --git a/app/components/tabs_component.html.erb b/app/components/tabs_component.html.erb index 9a1d938a..bfceddad 100644 --- a/app/components/tabs_component.html.erb +++ b/app/components/tabs_component.html.erb @@ -11,11 +11,6 @@ <% else %> <%= nav %> - <%= form_with url: cookie_session_path, scope: :cookie_session, method: :put, data: { tabs_target: "persistSelectionForm" } do |f| %> - <%= f.hidden_field :tab_key, value: "account_group_tab" %> - <%= f.hidden_field :tab_value, value: "all", data: { tabs_target: "selectionInput" } %> - <% end %> - <% panels.each do |panel| %> <%= panel %> <% end %> diff --git a/app/components/tabs_controller.js b/app/components/tabs_controller.js index 5b83b0ad..4fc5cf22 100644 --- a/app/components/tabs_controller.js +++ b/app/components/tabs_controller.js @@ -36,8 +36,22 @@ export default class extends Controller { // Update URL with the selected tab if (this.sessionKeyValue) { - this.selectionInputTarget.value = selectedTabId; - this.persistSelectionFormTarget.requestSubmit(); + this.#updateSessionPreference(selectedTabId); } } + + #updateSessionPreference(selectedTabId) { + fetch("/current_session", { + method: "PUT", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "X-CSRF-Token": document.querySelector('[name="csrf-token"]').content, + Accept: "application/json", + }, + body: new URLSearchParams({ + "current_session[tab_key]": this.sessionKeyValue, + "current_session[tab_value]": selectedTabId, + }).toString(), + }); + } } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8a1642f9..260579d1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,7 @@ class ApplicationController < ActionController::Base - include Onboardable, Localize, AutoSync, Authentication, Invitable, + include RestoreLayoutPreferences, Onboardable, Localize, AutoSync, Authentication, Invitable, SelfHostable, StoreLocation, Impersonatable, Breadcrumbable, - FeatureGuardable, Notifiable, AccountGroupable + FeatureGuardable, Notifiable include Pagy::Backend diff --git a/app/controllers/concerns/account_groupable.rb b/app/controllers/concerns/restore_layout_preferences.rb similarity index 58% rename from app/controllers/concerns/account_groupable.rb rename to app/controllers/concerns/restore_layout_preferences.rb index 6d4a41c3..284df4cc 100644 --- a/app/controllers/concerns/account_groupable.rb +++ b/app/controllers/concerns/restore_layout_preferences.rb @@ -1,13 +1,13 @@ -module AccountGroupable +module RestoreLayoutPreferences extend ActiveSupport::Concern included do - before_action :set_account_group_tab + before_action :restore_active_tabs end private - def set_account_group_tab - last_selected_tab = session["custom_account_group_tab"] || "asset" + def restore_active_tabs + last_selected_tab = Current.session&.get_preferred_tab("account_sidebar_tab") || "asset" @account_group_tab = account_group_tab_param || last_selected_tab end @@ -17,7 +17,7 @@ module AccountGroupable end def account_group_tab_param - param_value = params[:account_group_tab] + param_value = params[:account_sidebar_tab] return nil unless param_value.in?(valid_account_group_tabs) param_value end diff --git a/app/controllers/current_sessions_controller.rb b/app/controllers/current_sessions_controller.rb new file mode 100644 index 00000000..303b51f0 --- /dev/null +++ b/app/controllers/current_sessions_controller.rb @@ -0,0 +1,14 @@ +class CurrentSessionsController < ApplicationController + def update + if session_params[:tab_key].present? && session_params[:tab_value].present? + Current.session.set_preferred_tab(session_params[:tab_key], session_params[:tab_value]) + end + + head :ok + end + + private + def session_params + params.require(:current_session).permit(:tab_key, :tab_value) + end +end diff --git a/app/models/account/syncer.rb b/app/models/account/syncer.rb index fcb586b5..e21e25a1 100644 --- a/app/models/account/syncer.rb +++ b/app/models/account/syncer.rb @@ -13,7 +13,7 @@ class Account::Syncer def perform_post_sync account.family.auto_match_transfers! account.broadcast_refresh - account.family.broadcast_sidebar_refresh + SyncCompleteEvent.new(account.family, accounts: [ account ]).broadcast end private diff --git a/app/models/balance_sheet.rb b/app/models/balance_sheet.rb index 090775d4..cc50e3da 100644 --- a/app/models/balance_sheet.rb +++ b/app/models/balance_sheet.rb @@ -54,8 +54,11 @@ class BalanceSheet groups = account_groups.map do |accountable, accounts| group_total = accounts.sum(&:converted_balance) + key = accountable.model_name.param_key + AccountGroup.new( - key: accountable.model_name.param_key, + id: classification ? "#{classification}_#{key}_group" : "#{key}_group", + key: key, name: accountable.display_name, classification: accountable.classification, total: group_total, @@ -95,7 +98,7 @@ class BalanceSheet private ClassificationGroup = Struct.new(:key, :display_name, :icon, :total_money, :account_groups, :syncing?, keyword_init: true) - AccountGroup = Struct.new(:key, :name, :accountable_type, :classification, :total, :total_money, :weight, :accounts, :color, :missing_rates?, :syncing?, keyword_init: true) + AccountGroup = Struct.new(:id, :key, :name, :accountable_type, :classification, :total, :total_money, :weight, :accounts, :color, :missing_rates?, :syncing?, keyword_init: true) def active_accounts family.accounts.active.with_attached_logo diff --git a/app/models/family.rb b/app/models/family.rb index 4db7fdf0..0cfb8b89 100644 --- a/app/models/family.rb +++ b/app/models/family.rb @@ -44,14 +44,6 @@ class Family < ApplicationRecord .exists? end - def broadcast_sidebar_refresh - broadcast_replace( - target: "account-sidebar-tabs", - partial: "accounts/sidebar_tabs", - locals: { family: self, active_account_group_tab: "all" } - ) - end - def assigned_merchants merchant_ids = transactions.where.not(merchant_id: nil).pluck(:merchant_id).uniq Merchant.where(id: merchant_ids) diff --git a/app/models/plaid_item/syncer.rb b/app/models/plaid_item/syncer.rb index f6333403..51c3f32f 100644 --- a/app/models/plaid_item/syncer.rb +++ b/app/models/plaid_item/syncer.rb @@ -24,7 +24,7 @@ class PlaidItem::Syncer def perform_post_sync plaid_item.auto_match_categories! - plaid_item.family.broadcast_sidebar_refresh + SyncCompleteEvent.new(plaid_item.family, accounts: plaid_item.accounts).broadcast end private diff --git a/app/models/session.rb b/app/models/session.rb index ce26938a..66faa0f5 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -9,4 +9,14 @@ class Session < ApplicationRecord self.user_agent = Current.user_agent self.ip_address = Current.ip_address end + + def get_preferred_tab(tab_key) + data.dig("tab_preferences", tab_key) + end + + def set_preferred_tab(tab_key, tab_value) + data["tab_preferences"] ||= {} + data["tab_preferences"][tab_key] = tab_value + save! + end end diff --git a/app/models/sync.rb b/app/models/sync.rb index 14a46ed2..134b18c3 100644 --- a/app/models/sync.rb +++ b/app/models/sync.rb @@ -49,7 +49,9 @@ class Sync < ApplicationRecord begin syncable.perform_sync(self) rescue => e - fail_and_report_error(e) + fail! + update(error: e.message) + report_error(e) ensure finalize_if_all_children_finalized end @@ -64,9 +66,6 @@ class Sync < ApplicationRecord # If this is the "parent" and there are still children running, don't finalize. return unless all_children_finalized? - # If we make it here, the sync is finalized. Run post-sync, regardless of failure/success. - perform_post_sync - if syncing? if has_failed_children? fail! @@ -74,6 +73,9 @@ class Sync < ApplicationRecord complete! end end + + # If we make it here, the sync is finalized. Run post-sync, regardless of failure/success. + perform_post_sync end # If this sync has a parent, try to finalize it so the child status propagates up the chain. @@ -96,12 +98,10 @@ class Sync < ApplicationRecord def perform_post_sync syncable.perform_post_sync rescue => e - fail_and_report_error(e) + report_error(e) end - def fail_and_report_error(error) - fail! - update(error: error.message) + def report_error(error) Sentry.capture_exception(error) do |scope| scope.set_tags(sync_id: id) end diff --git a/app/models/sync_complete_event.rb b/app/models/sync_complete_event.rb new file mode 100644 index 00000000..44a17d37 --- /dev/null +++ b/app/models/sync_complete_event.rb @@ -0,0 +1,37 @@ +class SyncCompleteEvent + attr_reader :family, :accounts + + def initialize(family, accounts: []) + @family = family + @accounts = accounts + end + + def broadcast + account_groups = family.balance_sheet.account_groups.select do |group| + group.accounts.any? { |a| accounts.include?(a) } + end + + account_groups.each do |account_group| + [ nil, "asset", "liability" ].each do |classification| + id = classification ? "#{classification}_#{account_group.id}" : account_group.id + family.broadcast_replace( + target: id, + partial: "accounts/accountable_group", + locals: { account_group: account_group, open: true } + ) + end + end + + family.broadcast_replace( + target: "balance-sheet", + partial: "pages/dashboard/balance_sheet", + locals: { balance_sheet: family.balance_sheet } + ) + + family.broadcast_replace( + target: "net-worth-chart", + partial: "pages/dashboard/net_worth_chart", + locals: { balance_sheet: family.balance_sheet, period: Period.last_30_days } + ) + end +end diff --git a/app/views/accounts/_account_sidebar_tabs.html.erb b/app/views/accounts/_account_sidebar_tabs.html.erb index 74c7d705..056e7be1 100644 --- a/app/views/accounts/_account_sidebar_tabs.html.erb +++ b/app/views/accounts/_account_sidebar_tabs.html.erb @@ -1,4 +1,4 @@ -<%# locals: (family:, active_account_group_tab: "asset") %> +<%# locals: (family:, active_tab:, mobile: false) %>
<% if family.missing_data_provider? %> @@ -21,7 +21,7 @@ <% end %> - <%= render TabsComponent.new(active_tab: active_account_group_tab, session_key: "account_group_tab", testid: "account-sidebar-tabs") do |tabs| %> + <%= render TabsComponent.new(active_tab: active_tab, session_key: "account_sidebar_tab", testid: "account-sidebar-tabs") do |tabs| %> <% tabs.with_nav do |nav| %> <% nav.with_btn(id: "asset", label: "Assets") %> <% nav.with_btn(id: "liability", label: "Debts") %> @@ -42,7 +42,7 @@
<% family.balance_sheet.account_groups("asset").each do |group| %> - <%= render "accounts/accountable_group", account_group: group %> + <%= render "accounts/accountable_group", account_group: group, mobile: mobile %> <% end %>
@@ -62,7 +62,7 @@
<% family.balance_sheet.account_groups("liability").each do |group| %> - <%= render "accounts/accountable_group", account_group: group %> + <%= render "accounts/accountable_group", account_group: group, mobile: mobile %> <% end %>
@@ -82,7 +82,7 @@
<% family.balance_sheet.account_groups.each do |group| %> - <%= render "accounts/accountable_group", account_group: group %> + <%= render "accounts/accountable_group", account_group: group, mobile: mobile %> <% end %>
diff --git a/app/views/accounts/_accountable_group.html.erb b/app/views/accounts/_accountable_group.html.erb index 33fc42e8..26633918 100644 --- a/app/views/accounts/_accountable_group.html.erb +++ b/app/views/accounts/_accountable_group.html.erb @@ -1,67 +1,69 @@ -<%# locals: (account_group:) %> +<%# locals: (account_group:, mobile: false, open: nil, **args) %> -<%= render DisclosureComponent.new(title: account_group.name, align: :left, open: account_group.accounts.any? { |account| page_active?(account_path(account)) }) do |disclosure| %> - <% disclosure.with_summary_content do %> -
- <% if account_group.syncing? %> -
-
-
-
-
-
- <% else %> - <%= tag.p format_money(account_group.total_money), class: "text-sm font-medium text-primary" %> - <%= turbo_frame_tag "#{account_group.key}_sparkline", src: accountable_sparkline_path(account_group.key), loading: "lazy" do %> -
-
+
"> + <% is_open = open.nil? ? account_group.accounts.any? { |account| page_active?(account_path(account)) } : open %> + <%= render DisclosureComponent.new(title: account_group.name, align: :left, open: is_open) do |disclosure| %> + <% disclosure.with_summary_content do %> +
+ <% if account_group.syncing? %> +
+
+
+
+
+ <% else %> + <%= tag.p format_money(account_group.total_money), class: "text-sm font-medium text-primary" %> + <%= turbo_frame_tag "#{account_group.key}_sparkline", src: accountable_sparkline_path(account_group.key), loading: "lazy" do %> +
+
+
+ <% end %> <% end %> - <% end %> -
- <% end %> +
+ <% end %> -
- <% account_group.accounts.each do |account| %> - <%= link_to account_path(account), +
+ <% account_group.accounts.each do |account| %> + <%= link_to account_path(account), class: class_names( "block flex items-center gap-2 px-3 py-2 rounded-lg", page_active?(account_path(account)) ? "bg-container" : "hover:bg-surface-hover" ), title: account.name do %> - <%= render "accounts/logo", account: account, size: "sm", color: account_group.color %> + <%= render "accounts/logo", account: account, size: "sm", color: account_group.color %> -
- <%= tag.p account.name, class: "text-sm text-primary font-medium mb-0.5 truncate" %> - <%= tag.p account.short_subtype_label, class: "text-sm text-secondary truncate" %> -
+
+ <%= tag.p account.name, class: "text-sm text-primary font-medium mb-0.5 truncate" %> + <%= tag.p account.short_subtype_label, class: "text-sm text-secondary truncate" %> +
- <% if account.syncing? %> -
-
-
-
-
+ <% if account.syncing? %> +
+
+
+
+
+
-
- <% else %> -
- <%= tag.p format_money(account.balance_money), class: "text-sm font-medium text-primary whitespace-nowrap" %> + <% else %> +
+ <%= tag.p format_money(account.balance_money), class: "text-sm font-medium text-primary whitespace-nowrap" %> - <%= turbo_frame_tag dom_id(account, :sparkline), src: sparkline_account_path(account), loading: "lazy" do %> -
-
-
- <% end %> -
+ <%= turbo_frame_tag dom_id(account, :sparkline), src: sparkline_account_path(account), loading: "lazy" do %> +
+
+
+ <% end %> +
+ <% end %> <% end %> <% end %> - <% end %> -
+
-
- <%= render LinkComponent.new( +
+ <%= render LinkComponent.new( href: new_polymorphic_path(account_group.key, step: "method_select"), text: "New #{account_group.name.downcase.singularize}", icon: "plus", @@ -70,5 +72,6 @@ frame: :modal, class: "justify-start" ) %> -
-<% end %> +
+ <% end %> +
diff --git a/app/views/accounts/show/_chart.html.erb b/app/views/accounts/show/_chart.html.erb index 6a1dc545..0f63c9f1 100644 --- a/app/views/accounts/show/_chart.html.erb +++ b/app/views/accounts/show/_chart.html.erb @@ -1,4 +1,4 @@ -<%# locals: (account:, title: nil, tooltip: nil, chart_view: nil, **args) %> +<%# locals: (account:, tooltip: nil, chart_view: nil, **args) %> <% period = @period || Period.last_30_days %> <% default_value_title = account.asset? ? t(".balance") : t(".owed") %> @@ -7,10 +7,10 @@
- <%= tag.p title || default_value_title, class: "text-sm font-medium text-secondary" %> + <%= tag.p account.investment? ? "Total value" : default_value_title, class: "text-sm font-medium text-secondary" %> - <% unless account.syncing? %> - <%= tooltip %> + <% if !account.syncing? && account.investment? %> + <%= render "investments/value_tooltip", balance: account.balance_money, holdings: account.balance_money - account.cash_balance_money, cash: account.cash_balance_money %> <% end %>
diff --git a/app/views/accounts/show/_template.html.erb b/app/views/accounts/show/_template.html.erb index cfac9402..51327254 100644 --- a/app/views/accounts/show/_template.html.erb +++ b/app/views/accounts/show/_template.html.erb @@ -1,4 +1,4 @@ -<%# locals: (account:, header: nil, chart: nil, tabs: nil) %> +<%# locals: (account:, header: nil, chart: nil, chart_view: nil, tabs: nil) %> <%= turbo_stream_from account %> @@ -13,7 +13,7 @@ <% if chart.present? %> <%= chart %> <% else %> - <%= render "accounts/show/chart", account: account %> + <%= render "accounts/show/chart", account: account, chart_view: chart_view %> <% end %>
diff --git a/app/views/investments/show.html.erb b/app/views/investments/show.html.erb index 7bd7da3b..84be8629 100644 --- a/app/views/investments/show.html.erb +++ b/app/views/investments/show.html.erb @@ -1,25 +1,7 @@ -<%= turbo_stream_from @account %> - -<%= turbo_frame_tag dom_id(@account) do %> - <%= tag.div class: "space-y-4" do %> - <%= render "accounts/show/header", account: @account %> - - <%= render "accounts/show/chart", - account: @account, - title: t(".chart_title"), - chart_view: @chart_view, - tooltip: render( - "investments/value_tooltip", - balance: @account.balance_money, - holdings: @account.balance_money - @account.cash_balance_money, - cash: @account.cash_balance_money - ) %> - -
- <%= render "accounts/show/tabs", account: @account, tabs: [ - { key: "activity", contents: render("accounts/show/activity", account: @account) }, - { key: "holdings", contents: render("investments/holdings_tab", account: @account) }, - ] %> -
- <% end %> -<% end %> +<%= render "accounts/show/template", + account: @account, + chart_view: @chart_view, + tabs: render("accounts/show/tabs", account: @account, tabs: [ + { key: "activity", contents: render("accounts/show/activity", account: @account) }, + { key: "holdings", contents: render("investments/holdings_tab", account: @account) }, + ]) %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 4b6ff347..e8fc202e 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -26,7 +26,8 @@ <%= render( "accounts/account_sidebar_tabs", family: Current.family, - active_account_group_tab: @account_group_tab + active_tab: @account_group_tab, + mobile: true ) %>
@@ -81,7 +82,7 @@ <% else %>
- <%= render "accounts/account_sidebar_tabs", family: Current.family, active_account_group_tab: @account_group_tab %> + <%= render "accounts/account_sidebar_tabs", family: Current.family, active_tab: @account_group_tab %>
<% if Current.family.trialing? && !self_hosted? %> diff --git a/app/views/pages/dashboard/_balance_sheet.html.erb b/app/views/pages/dashboard/_balance_sheet.html.erb index c3606614..6b4ea525 100644 --- a/app/views/pages/dashboard/_balance_sheet.html.erb +++ b/app/views/pages/dashboard/_balance_sheet.html.erb @@ -1,6 +1,6 @@ -<%# locals: (balance_sheet:) %> +<%# locals: (balance_sheet:, **args) %> -
+
<% balance_sheet.classification_groups.each do |classification_group| %>

diff --git a/app/views/pages/dashboard/_net_worth_chart.html.erb b/app/views/pages/dashboard/_net_worth_chart.html.erb index 4d1e443a..a6b65852 100644 --- a/app/views/pages/dashboard/_net_worth_chart.html.erb +++ b/app/views/pages/dashboard/_net_worth_chart.html.erb @@ -1,54 +1,55 @@ -<%# locals: (balance_sheet:, period:) %> +<%# locals: (balance_sheet:, period:, **args) %> -<% series = balance_sheet.net_worth_series(period: period) %> - -
-
+
+ <% series = balance_sheet.net_worth_series(period: period) %> +
-

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

+
+

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

- <% if balance_sheet.syncing? %> -
-
-
-
- <% else %> -

- <%= series.current.format %> -

- - <% if series.trend.nil? %> -

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

+ <% if balance_sheet.syncing? %> +
+
+
+
<% else %> - <%= render partial: "shared/trend_change", locals: { trend: series.trend, comparison_label: period.comparison_label } %> - <% end %> - <% end %> -
-
+

+ <%= series.current.format %> +

- <%= form_with url: root_path, method: :get, data: { controller: "auto-submit-form" } do |form| %> - <%= form.select :period, + <% if series.trend.nil? %> +

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

+ <% else %> + <%= render partial: "shared/trend_change", locals: { trend: series.trend, comparison_label: period.comparison_label } %> + <% end %> + <% end %> +
+
+ + <%= form_with url: root_path, method: :get, data: { controller: "auto-submit-form" } do |form| %> + <%= form.select :period, Period.as_options, { selected: period.key }, data: { "auto-submit-form-target": "auto" }, class: "bg-container border border-secondary font-medium rounded-lg px-3 py-2 text-sm pr-7 cursor-pointer text-primary focus:outline-hidden focus:ring-0" %> - <% end %> -
- -<% if balance_sheet.syncing? %> -
-
+ <% end %>
-<% else %> - <% if series.any? %> -
+
+
+
+ <% else %> + <% if series.any? %> +
- <% else %> -
-

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

-
+ <% else %> +
+

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

+
+ <% end %> <% end %> -<% end %> +
diff --git a/config/routes.rb b/config/routes.rb index 1d68af04..ec9e2cce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,7 +26,7 @@ Rails.application.routes.draw do get "changelog", to: "pages#changelog" get "feedback", to: "pages#feedback" - resource :cookie_session, only: %i[update] + resource :current_session, only: %i[update] resource :registration, only: %i[new create] resources :sessions, only: %i[new create destroy] diff --git a/db/migrate/20250514214242_add_metadata_to_session.rb b/db/migrate/20250514214242_add_metadata_to_session.rb new file mode 100644 index 00000000..849cdccf --- /dev/null +++ b/db/migrate/20250514214242_add_metadata_to_session.rb @@ -0,0 +1,5 @@ +class AddMetadataToSession < ActiveRecord::Migration[7.2] + def change + add_column :sessions, :data, :jsonb, default: {} + end +end diff --git a/db/schema.rb b/db/schema.rb index 10ac65c8..7f25cb20 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_05_13_122703) do +ActiveRecord::Schema[7.2].define(version: 2025_05_14_214242) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -537,6 +537,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_05_13_122703) do t.uuid "active_impersonator_session_id" t.datetime "subscribed_at" t.jsonb "prev_transaction_page_params", default: {} + t.jsonb "data", default: {} t.index ["active_impersonator_session_id"], name: "index_sessions_on_active_impersonator_session_id" t.index ["user_id"], name: "index_sessions_on_user_id" end diff --git a/test/controllers/current_sessions_controller_test.rb b/test/controllers/current_sessions_controller_test.rb new file mode 100644 index 00000000..9d498fb3 --- /dev/null +++ b/test/controllers/current_sessions_controller_test.rb @@ -0,0 +1,15 @@ +require "test_helper" + +class CurrentSessionsControllerTest < ActionDispatch::IntegrationTest + setup do + @user = users(:family_admin) + sign_in @user + end + + test "can update the preferred tab for any namespace" do + put current_session_url, params: { current_session: { tab_key: "accounts_sidebar_tab", tab_value: "asset" } } + assert_response :success + session = Session.order(updated_at: :desc).first + assert_equal "asset", session.get_preferred_tab("accounts_sidebar_tab") + end +end