diff --git a/app/components/DS/dialog.html.erb b/app/components/DS/dialog.html.erb index 72b8c0740..a26b0ae7c 100644 --- a/app/components/DS/dialog.html.erb +++ b/app/components/DS/dialog.html.erb @@ -1,16 +1,14 @@ <%= wrapper_element do %> - <%= tag.dialog class: "w-full h-full bg-transparent theme-dark:backdrop:bg-alpha-black-900 backdrop:bg-overlay pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)] #{drawer? ? "lg:p-3" : "lg:p-1"}", **merged_opts do %> + <%= tag.dialog class: "w-full h-full bg-transparent theme-dark:backdrop:bg-alpha-black-900 backdrop:bg-overlay pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)] #{(drawer? || responsive?) ? "lg:p-3" : "lg:p-1"}", **merged_opts do %> <%= tag.div class: dialog_outer_classes do %> <%= tag.div class: dialog_inner_classes, data: { DS__dialog_target: "content" } do %>
<% if header? %> <%= header %> <% end %> - <% if body? %>
<%= body %> - <% if sections.any? %>
<% sections.each do |section| %> @@ -20,11 +18,9 @@ <% end %>
<% end %> - <%# Optional, for customizing dialogs %> <%= content %>
- <% if actions? %>
<% actions.each do |action| %> diff --git a/app/components/DS/dialog.rb b/app/components/DS/dialog.rb index a8dba3d0d..536febf6c 100644 --- a/app/components/DS/dialog.rb +++ b/app/components/DS/dialog.rb @@ -1,9 +1,9 @@ class DS::Dialog < DesignSystemComponent - renders_one :header, ->(title: nil, subtitle: nil, hide_close_icon: false, **opts, &block) do + renders_one :header, ->(title: nil, subtitle: nil, custom_header: false, **opts, &block) do content_tag(:header, class: "px-4 flex flex-col gap-2", **opts) do title_div = content_tag(:div, class: "flex items-center justify-between gap-2") do title = content_tag(:h2, title, class: class_names("font-medium text-primary", drawer? ? "text-lg" : "")) if title - close_icon = render DS::Button.new(variant: "icon", class: "ml-auto", icon: "x", tabindex: "-1", data: { action: "DS--dialog#close" }) unless hide_close_icon + close_icon = close_button unless custom_header safe_join([ title, close_icon ].compact) end @@ -33,7 +33,7 @@ class DS::Dialog < DesignSystemComponent end end - attr_reader :variant, :auto_open, :reload_on_close, :width, :disable_frame, :content_class, :disable_click_outside, :opts + attr_reader :variant, :auto_open, :reload_on_close, :width, :disable_frame, :content_class, :disable_click_outside, :opts, :responsive VARIANTS = %w[modal drawer].freeze WIDTHS = { @@ -43,7 +43,7 @@ class DS::Dialog < DesignSystemComponent full: "lg:max-w-full" }.freeze - def initialize(variant: "modal", auto_open: true, reload_on_close: false, width: "md", frame: nil, disable_frame: false, content_class: nil, disable_click_outside: false, **opts) + def initialize(variant: "modal", auto_open: true, reload_on_close: false, width: "md", frame: nil, disable_frame: false, content_class: nil, disable_click_outside: false, responsive: false, **opts) @variant = variant.to_sym @auto_open = auto_open @reload_on_close = reload_on_close @@ -52,6 +52,7 @@ class DS::Dialog < DesignSystemComponent @disable_frame = disable_frame @content_class = content_class @disable_click_outside = disable_click_outside + @responsive = responsive @opts = opts end @@ -69,7 +70,9 @@ class DS::Dialog < DesignSystemComponent end def dialog_outer_classes - variant_classes = if drawer? + variant_classes = if responsive? + "items-center justify-center lg:items-end lg:justify-end" + elsif drawer? "items-end justify-end" else "items-center justify-center" @@ -82,7 +85,9 @@ class DS::Dialog < DesignSystemComponent end def dialog_inner_classes - variant_classes = if drawer? + variant_classes = if responsive? + "max-h-full lg:h-full lg:w-[550px]" + elsif drawer? "lg:w-[550px] h-full" else class_names( @@ -116,4 +121,20 @@ class DS::Dialog < DesignSystemComponent def drawer? variant == :drawer end + + def responsive? + @responsive + end + + def close_button + classes = responsive? ? "ml-auto hidden lg:flex" : "ml-auto" + render DS::Button.new( + variant: "icon", + class: classes, + icon: "x", + title: I18n.t("common.close"), + aria_label: I18n.t("common.close"), + data: { action: "DS--dialog#close" } + ) + end end diff --git a/app/views/holdings/show.html.erb b/app/views/holdings/show.html.erb index 67b4329ba..927d0b9a2 100644 --- a/app/views/holdings/show.html.erb +++ b/app/views/holdings/show.html.erb @@ -1,21 +1,22 @@ -<%= render DS::Dialog.new(frame: "drawer") do |dialog| %> - <% dialog.with_header(hide_close_icon: true) do %> +<%= render DS::Dialog.new(frame: "drawer", responsive: true) do |dialog| %> + <% dialog.with_header(custom_header: true) do %>
<%= tag.h3 @holding.name, class: "text-2xl font-medium text-primary" %> <%= tag.p @holding.ticker, class: "text-sm text-secondary" %>
- - <% if @holding.security.brandfetch_icon_url.present? %> - <%= image_tag @holding.security.brandfetch_icon_url, loading: "lazy", class: "w-9 h-9 rounded-full" %> - <% elsif @holding.security.logo_url.present? %> - <%= image_tag @holding.security.logo_url, loading: "lazy", class: "w-9 h-9 rounded-full" %> - <% else %> - <%= render DS::FilledIcon.new(variant: :text, text: @holding.name, size: "md", rounded: true) %> - <% end %> +
+ <% if @holding.security.brandfetch_icon_url.present? %> + <%= image_tag @holding.security.brandfetch_icon_url, loading: "lazy", class: "w-9 h-9 rounded-full" %> + <% elsif @holding.security.logo_url.present? %> + <%= image_tag @holding.security.logo_url, loading: "lazy", class: "w-9 h-9 rounded-full" %> + <% else %> + <%= render DS::FilledIcon.new(variant: :text, text: @holding.name, size: "md", rounded: true) %> + <% end %> + <%= dialog.close_button %> +
<% end %> - <% dialog.with_body do %> <% dialog.with_section(title: t(".overview"), open: true) do %>
@@ -33,13 +34,12 @@ <% end %>
-
-
<%= t(".current_market_price_label") %>
<% begin %> <%= @holding.security.current_price ? format_money(@holding.security.current_price) : t(".unknown") %> - <% rescue ActiveRecord::RecordInvalid, StandardError %> + <% rescue ActiveRecord::RecordInvalid %> + <%= t(".unknown") %> + <% rescue StandardError => e %> + <% logger.error "Error fetching current price for security #{@holding.security.id}: #{e.message}" %> + <% logger.error e.backtrace.first(5).join("\n") %> <%= t(".unknown") %> <% end %>
-
<%= t(".portfolio_weight_label") %>
<%= @holding.weight ? number_to_percentage(@holding.weight, precision: 2) : t(".unknown") %>
- <%# Average Cost with inline editor %> <% currency = Money::Currency.new(@holding.currency) @@ -103,7 +104,6 @@
- <%# Inline cost basis editor (hidden by default) %> @@ -144,7 +144,6 @@

= <%= currency.symbol %><%= number_with_precision(current_per_share, precision: 2) || "0.00" %> <%= t("holdings.cost_basis_cell.per_share") %>

-
@@ -156,11 +155,10 @@ autocomplete="off" value="<%= number_with_precision(current_per_share, precision: 2) if current_per_share %>" data-action="input->drawer-cost-basis#updateTotal" - data-drawer-cost-basis-target="perShare"> + data-drawer-cost-basis-target="perShare"> <%= currency.iso_code %>
-
-
<%= t(".total_return_label") %>
<% if @holding.trend %> @@ -186,7 +183,6 @@
<% end %> - <% dialog.with_section(title: t(".history"), open: true) do %>
@@ -200,10 +196,8 @@
<% end %>
-

<%= l(trade_entry.date, format: :long) %>

-

<%= t( ".trade_history_entry", qty: trade_entry.trade.qty, @@ -214,14 +208,12 @@ <% end %> - <% else %>

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

<% end %>
<% end %> - <% if @holding.cost_basis_locked? || @holding.security_remapped? || @holding.account.can_delete_holdings? %> <% dialog.with_section(title: t(".settings"), open: true) do %>
@@ -231,7 +223,6 @@

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

<%= t(".provider_sent", ticker: @holding.provider_security.ticker) %>

- <%= button_to t(".reset_to_provider"), reset_security_holding_path(@holding), method: :post, @@ -243,14 +234,12 @@ } } %> <% end %> - <% if @holding.cost_basis_locked? %>

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

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

- <%= button_to t(".unlock_cost_basis"), unlock_cost_basis_holding_path(@holding), method: :post, @@ -259,14 +248,12 @@ data: { turbo_confirm: { title: t(".unlock_confirm_title"), body: t(".unlock_confirm_body") } } %>
<% end %> - <% if @holding.account.can_delete_holdings? %>

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

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

- <%= button_to t(".delete"), holding_path(@holding), method: :delete, diff --git a/app/views/pages/dashboard/_cashflow_sankey.html.erb b/app/views/pages/dashboard/_cashflow_sankey.html.erb index ae9904eb9..fe15a4fdc 100644 --- a/app/views/pages/dashboard/_cashflow_sankey.html.erb +++ b/app/views/pages/dashboard/_cashflow_sankey.html.erb @@ -9,7 +9,6 @@ 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 sankey_data[:links].present? %>
- <%= render DS::Dialog.new(id: "cashflow-expanded-dialog", auto_open: false, width: "custom", disable_frame: true, content_class: "!w-[96vw] max-w-[1650px]", data: { action: "close->cashflow-expand#restore" }) do |dialog| %> - <% dialog.with_header(title: t("pages.dashboard.cashflow_sankey.title"), hide_close_icon: false) %> + <% dialog.with_header(title: t("pages.dashboard.cashflow_sankey.title")) %> <% dialog.with_body do %>
-

<%= t("pages.dashboard.cashflow_sankey.no_data_title") %>

<%= t("pages.dashboard.cashflow_sankey.no_data_description") %>

<%= render DS::Link.new( diff --git a/app/views/trades/show.html.erb b/app/views/trades/show.html.erb index d5eea69f6..75c36f527 100644 --- a/app/views/trades/show.html.erb +++ b/app/views/trades/show.html.erb @@ -1,13 +1,13 @@ -<%= render DS::Dialog.new(frame: "drawer") do |dialog| %> - <% dialog.with_header(hide_close_icon: true) do %> - <%= render "trades/header", entry: @entry %> +<%= render DS::Dialog.new(frame: "drawer", responsive: true) do |dialog| %> + <% dialog.with_header(custom_header: true) do %> +
+ <%= render "trades/header", entry: @entry %> + <%= dialog.close_button %> +
<% end %> - <% trade = @entry.trade %> - <% dialog.with_body do %> <%= render "entries/protection_indicator", entry: @entry, unlock_path: unlock_trade_path(trade) %> - <% dialog.with_section(title: t(".details"), open: true) do %>
<%= styled_form_with model: @entry, @@ -19,13 +19,11 @@ max: Date.current, disabled: @entry.linked?, "data-auto-submit-form-target": "auto" %> -
<%= f.select :nature, [[t(".buy"), "outflow"], [t(".sell"), "inflow"]], { container_class: "w-1/3", label: t(".type_label"), selected: @entry.amount.negative? ? "outflow" : "inflow" }, { data: { "auto-submit-form-target": "auto" }, disabled: @entry.linked? } %> - <%= f.fields_for :entryable do |ef| %> <%= ef.number_field :qty, label: t(".quantity_label"), @@ -35,7 +33,6 @@ disabled: @entry.linked? %> <% end %>
- <%= f.fields_for :entryable do |ef| %> <%= ef.money_field :price, label: t(".cost_per_share_label"), @@ -49,7 +46,6 @@ <% end %>
<% end %> - <% dialog.with_section(title: t(".additional")) do %>
<%= styled_form_with model: @entry, @@ -64,7 +60,6 @@ <% end %>
<% end %> - <% dialog.with_section(title: t(".settings")) do %>
@@ -77,18 +72,15 @@

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

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

- <%= f.toggle :excluded, { data: { auto_submit_form_target: "auto" } } %>
<% end %> -

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

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

- <%= button_to t(".delete"), entry_path(@entry), method: :delete, diff --git a/app/views/transactions/_header.html.erb b/app/views/transactions/_header.html.erb index 3ad9530ae..a64898dda 100644 --- a/app/views/transactions/_header.html.erb +++ b/app/views/transactions/_header.html.erb @@ -1,32 +1,26 @@ <%# locals: (entry:) %> -

<%= format_money -entry.amount_money %> - <%= entry.currency %> - <% if entry.transaction.transfer? %> <%= icon "arrow-left-right", size: "sm", class: "text-secondary" %> <% end %> - <% if entry.linked? %> <%= icon("refresh-ccw", size: "sm") %> <% end %>

-
<%= I18n.l(entry.date, format: :long) %> - <% if entry.transaction.pending? %> "> <%= icon "clock", size: "sm", color: "current" %> @@ -35,6 +29,4 @@ <% end %>
- - <%= render DS::Button.new(variant: "icon", icon: "x", data: { action: "DS--dialog#close" }) %>
diff --git a/app/views/transactions/show.html.erb b/app/views/transactions/show.html.erb index f2914d9b8..3dc25b148 100644 --- a/app/views/transactions/show.html.erb +++ b/app/views/transactions/show.html.erb @@ -1,8 +1,10 @@ -<%= render DS::Dialog.new(frame: "drawer") do |dialog| %> - <% dialog.with_header(hide_close_icon: true) do %> - <%= render "transactions/header", entry: @entry %> +<%= render DS::Dialog.new(frame: "drawer", responsive: true) do |dialog| %> + <% dialog.with_header(custom_header: true) do %> +
+ <%= render "transactions/header", entry: @entry %> + <%= dialog.close_button %> +
<% end %> - <% dialog.with_body do %> <%# Potential duplicate alert %> <% if @entry.transaction.has_potential_duplicate? %> @@ -14,7 +16,6 @@

<%= t("transactions.show.potential_duplicate_title") %>

<%= t("transactions.show.potential_duplicate_description") %>

-
@@ -26,7 +27,6 @@

-
<%= button_to t("transactions.show.merge_duplicate"), merge_duplicate_transaction_path(@entry.transaction), @@ -44,33 +44,27 @@
<% end %> <% end %> - <%= render "entries/protection_indicator", entry: @entry, unlock_path: unlock_transaction_path(@entry.transaction) %> - <% dialog.with_section(title: t(".overview"), open: true) do %>
<%= styled_form_with model: @entry, url: transaction_path(@entry), class: "space-y-2", data: { controller: "auto-submit-form" } do |f| %> - <%= f.text_field :name, label: t(".name_label"), "data-auto-submit-form-target": "auto" %> - <%= f.date_field :date, label: t(".date_label"), max: Date.current, disabled: @entry.linked?, "data-auto-submit-form-target": "auto" %> - <% unless @entry.transaction.transfer? %>
<%= f.select :nature, [["Expense", "outflow"], ["Income", "inflow"]], { container_class: "w-1/3", label: t(".nature"), selected: @entry.amount.negative? ? "inflow" : "outflow" }, { data: { "auto-submit-form-target": "auto" }, disabled: @entry.linked? } %> - <%= f.money_field :amount, label: t(".amount"), container_class: "w-2/3", auto_submit: true, @@ -79,7 +73,6 @@ disabled: @entry.linked?, disable_currency: @entry.linked? %>
- <%= f.fields_for :entryable do |ef| %> <%= ef.collection_select :category_id, Current.family.categories.alphabetically, @@ -89,11 +82,9 @@ "data-auto-submit-form-target": "auto" %> <% end %> <% end %> - <% end %>
<% end %> - <% dialog.with_section(title: t(".details")) do %> <%= styled_form_with model: @entry, url: transaction_path(@entry), @@ -107,9 +98,7 @@ ), { label: t(".account_label") }, { disabled: true } %> - <%= f.fields_for :entryable do |ef| %> - <%= ef.collection_select :merchant_id, Current.family.available_merchants.alphabetically, :id, :name, @@ -117,7 +106,6 @@ label: t(".merchant_label"), class: "text-subdued" }, "data-auto-submit-form-target": "auto" %> - <%= ef.select :tag_ids, Current.family.tags.alphabetically.pluck(:name, :id), { @@ -128,16 +116,13 @@ { "data-controller": "multi-select", "data-auto-submit-form-target": "auto" } %> <% end %> <% end %> - <%= f.text_area :notes, label: t(".note_label"), placeholder: t(".note_placeholder"), rows: 5, "data-auto-submit-form-target": "auto" %> - <% end %> <% end %> - <% if (details = build_transaction_extra_details(@entry)) %> <% dialog.with_section(title: "Additional details", open: false) do %>
@@ -165,7 +150,6 @@ <% end %> <% end %> - <% if details[:provider_extras].present? %>

Provider extras

@@ -185,7 +169,6 @@
<% end %> <% end %> - <% dialog.with_section(title: t(".settings")) do %>
<%= styled_form_with model: @entry, @@ -197,12 +180,10 @@

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

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

- <%= f.toggle :excluded, { data: { auto_submit_form_target: "auto" } } %>
<% end %>
- <% if @entry.account.investment? || @entry.account.crypto? %>
<%= styled_form_with model: @entry, @@ -215,7 +196,6 @@

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

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

- <%= ef.select :investment_activity_label, options_for_select( [["—", nil]] + Transaction::ACTIVITY_LABELS.map { |l| [t("transactions.activity_labels.#{l.parameterize(separator: '_')}"), l] }, @@ -229,7 +209,6 @@ <% end %>
<% end %> -
<%= styled_form_with model: @entry, url: transaction_path(@entry), @@ -241,7 +220,6 @@

<%= t(".one_time_title", type: @entry.amount.negative? ? t("transactions.form.income") : t("transactions.form.expense")) %>

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

- <%= ef.toggle :kind, { checked: @entry.transaction.one_time?, data: { auto_submit_form_target: "auto" } @@ -249,13 +227,11 @@
<% end %> <% end %> -

Transfer or Debt Payment?

Transfers and payments are special types of transactions that indicate money movement between 2 accounts.

- <%= render DS::Link.new( text: "Open matcher", icon: "arrow-left-right", @@ -264,7 +240,6 @@ frame: :modal ) %>
- <% if @entry.account.investment? && @entry.entryable.is_a?(Transaction) && !@entry.excluded? %>
@@ -272,7 +247,6 @@

Convert to Security Trade

Convert this transaction into a security trade (buy/sell) by providing ticker, shares, and price.

- <%= render DS::Button.new( text: "Convert", variant: "outline", @@ -283,14 +257,12 @@ ) %>
<% end %> -

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

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

- <%= render DS::Button.new( text: t(".mark_recurring"), variant: "outline", @@ -300,14 +272,12 @@ frame: "_top" ) %>
-

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

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

- <%= render DS::Button.new( text: t(".delete"), variant: "outline-destructive", diff --git a/app/views/transfers/show.html.erb b/app/views/transfers/show.html.erb index f8a8248eb..5a369a2c6 100644 --- a/app/views/transfers/show.html.erb +++ b/app/views/transfers/show.html.erb @@ -1,28 +1,23 @@ -<%= render DS::Dialog.new(frame: "drawer") do |dialog| %> - <% dialog.with_header(hide_close_icon: true) do %> +<%= render DS::Dialog.new(frame: "drawer", responsive: true) do |dialog| %> + <% dialog.with_header(custom_header: true) do %>

<%= format_money @transfer.amount_abs %> - <%= @transfer.amount_abs.currency.iso_code %> - <%= icon "arrow-left-right", size: "sm", class: "text-secondary" %>

- <%= @transfer.name %>
- - <%= render DS::Button.new(variant: "icon", icon: "x", data: { action: "DS--dialog#close" }) %> + <%= dialog.close_button %>
<% end %> - <% dialog.with_body do %> <% dialog.with_section(title: t(".overview"), open: true) do %>
@@ -34,20 +29,16 @@ <%= link_to @transfer.from_account.name, account_path(@transfer.from_account), data: { turbo_frame: "_top" } %> -
Date
<%= l(@transfer.outflow_transaction.entry.date, format: :long) %>
-
Amount
<%= format_money @transfer.outflow_transaction.entry.amount_money * -1 %>
- <%= render "shared/ruler", classes: "my-2" %> -
To
@@ -56,12 +47,10 @@ <%= link_to @transfer.to_account.name, account_path(@transfer.to_account), data: { turbo_frame: "_top" } %>
-
Date
<%= l(@transfer.inflow_transaction.entry.date, format: :long) %>
-
Amount
+<%= format_money @transfer.inflow_transaction.entry.amount_money * -1 %>
@@ -69,14 +58,12 @@
<% end %> - <% dialog.with_section(title: t(".details")) do %> <%= styled_form_with model: @transfer, data: { controller: "auto-submit-form" }, class: "space-y-2" do |f| %> <% if @transfer.categorizable? %> <%= f.collection_select :category_id, @categories.alphabetically, :id, :name, { label: "Category", include_blank: "Uncategorized", selected: @transfer.outflow_transaction.category&.id }, "data-auto-submit-form-target": "auto" %> <% end %> - <%= f.text_area :notes, label: t(".note_label"), placeholder: t(".note_placeholder"), @@ -84,7 +71,6 @@ "data-auto-submit-form-target": "auto" %> <% end %> <% end %> - <% dialog.with_section(title: t(".settings")) do %>
@@ -92,7 +78,6 @@

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

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

- <%= button_to t(".delete"), transfer_path(@transfer), method: :delete, diff --git a/app/views/valuations/show.html.erb b/app/views/valuations/show.html.erb index a5977c23c..1cfaa09f9 100644 --- a/app/views/valuations/show.html.erb +++ b/app/views/valuations/show.html.erb @@ -1,17 +1,17 @@ <% entry, account = @entry, @entry.account %> - -<%= render DS::Dialog.new(frame: "drawer") do |dialog| %> - <% dialog.with_header(hide_close_icon: true) do %> - <%= render "valuations/header", entry: @entry %> +<%= render DS::Dialog.new(frame: "drawer", responsive: true) do |dialog| %> + <% dialog.with_header(custom_header: true) do %> +
+ <%= render "valuations/header", entry: @entry %> + <%= dialog.close_button %> +
<% end %> - <% dialog.with_body do %> <% if @error_message.present? %>
<%= render DS::Alert.new(message: @error_message, variant: :error) %>
<% end %> - <% dialog.with_section(title: t(".overview"), open: true) do %>
<%= styled_form_with model: entry, @@ -22,11 +22,9 @@ <%= f.date_field :date, label: t(".date_label"), max: Date.current %> - <%= f.money_field :amount, label: "Account value on date", disable_currency: true %> -
<%= render DS::Button.new( text: "Update value", @@ -37,7 +35,6 @@ <% end %>
<% end %> - <% dialog.with_section(title: t(".details")) do %>
<%= styled_form_with model: entry, @@ -53,7 +50,6 @@ <% end %>
<% end %> - <% dialog.with_section(title: t(".settings")) do %>
@@ -62,7 +58,6 @@

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

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

- <%= button_to t(".delete"), entry_path(entry), method: :delete,