refactor(settings/providers): drawer cleanup, header lock-up, trust statement

Per the design review's §07.

- Drop the trailing "Configured / Not configured" footer status from
  every provider panel (binance, coinbase, coinstats, indexa_capital,
  lunchflow, mercury, simplefin, snaptrade, sophtron, provider_form).
  The parent details section's status pill already carries that
  signal; the footer was redundant — and the copy/styling was
  inconsistent across panels (free-text vs. dot pill, "configured"
  vs. "not connected").
- Connect drawer gets a header lock-up: small logo chip + provider
  name + maturity badge, mirroring the available-card layout.
  Implemented as _drawer_header partial; connect_form passes
  custom_header: true to DS::Dialog so we own the row.
- Drawer footer trust statement: "Read-only — Sure can never move
  money. Stored encrypted." A single-line reassurance covering all
  panels.
- Sentence-case the hardcoded primary buttons that were Title Case:
  "Save Configuration" -> "Save and connect"
  "Update Configuration" -> "Update connection"
  "Connect Bank" -> "Connect bank"
  Affects simplefin, lunchflow, enable_banking, provider_form. The
  i18n'd panels (binance, coinbase, coinstats, indexa_capital,
  mercury, snaptrade, sophtron) keep their existing keys.
This commit is contained in:
Guillem Arias
2026-05-09 11:47:40 +02:00
committed by Guillem Arias
parent 4899d98c75
commit 89726e6d82
14 changed files with 76 additions and 141 deletions

View File

@@ -94,13 +94,4 @@
<% end %>
<% end %>
<div class="flex items-center gap-2">
<% if items.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary"><%= t("settings.providers.binance_panel.status_connected") %></p>
<% else %>
<div class="w-2 h-2 bg-tertiary rounded-full"></div>
<p class="text-sm text-secondary"><%= t("settings.providers.binance_panel.status_not_connected") %></p>
<% end %>
</div>
</div>

View File

@@ -82,13 +82,4 @@
<% end %>
<% end %>
<div class="flex items-center gap-2">
<% if items.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary"><%= t("settings.providers.coinbase_panel.status_connected") %></p>
<% else %>
<div class="w-2 h-2 bg-tertiary rounded-full"></div>
<p class="text-sm text-secondary"><%= t("settings.providers.coinbase_panel.status_not_connected") %></p>
<% end %>
</div>
</div>

View File

@@ -41,14 +41,4 @@
</div>
<% end %>
<% items = local_assigns[:coinstats_items] || @coinstats_items || Current.family.coinstats_items.where.not(api_key: [nil, ""]) %>
<div class="flex items-center gap-2">
<% if items&.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary"><%= t("coinstats_items.new.status_configured_html", accounts_url: accounts_path).html_safe %></p>
<% else %>
<div class="w-2 h-2 bg-gray-400 rounded-full"></div>
<p class="text-sm text-secondary"><%= t("coinstats_items.new.status_not_configured") %></p>
<% end %>
</div>
</div>

View File

@@ -0,0 +1,25 @@
<%# locals: (provider_key:, title:) %>
<% meta = provider_key.present? ? Provider::Metadata.for(provider_key) : nil %>
<% maturity_label_key = meta && Settings::ProviderCard::MATURITY_LABELS[meta[:maturity]] %>
<% maturity_label = maturity_label_key ? t(maturity_label_key) : nil %>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 min-w-0">
<% if meta && meta[:logo_bg].present? %>
<span class="w-7 h-7 rounded-lg flex items-center justify-center shrink-0 <%= meta[:logo_bg] %>">
<span class="text-[10px] font-bold text-inverse"><%= meta[:logo_text] %></span>
</span>
<% end %>
<h2 class="text-lg font-medium text-primary truncate"><%= title %></h2>
<% if maturity_label %>
<span class="text-xs font-medium px-1.5 py-0.5 rounded-full bg-alpha-black-50 text-secondary shrink-0"><%= maturity_label %></span>
<% end %>
</div>
<%= render DS::Button.new(
variant: "icon",
class: "ml-auto hidden lg:flex",
icon: "x",
title: t("common.close"),
aria_label: t("common.close"),
data: { action: "DS--dialog#close" }
) %>
</div>

View File

@@ -98,7 +98,7 @@
disabled: has_authenticated_connections && !is_new_record %>
<div class="flex justify-end">
<%= form.submit is_new_record ? "Save Configuration" : "Update Configuration",
<%= form.submit is_new_record ? "Save and connect" : "Update connection",
class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors" %>
</div>
<% end %>
@@ -169,7 +169,7 @@
<%= link_to select_bank_enable_banking_item_path(item),
class: "inline-flex items-center justify-center rounded-lg px-3 py-1.5 text-xs font-medium text-inverse button-bg-primary hover:button-bg-primary-hover transition-colors",
data: { turbo_frame: "modal" } do %>
Connect Bank
Connect bank
<% end %>
<% end %>

View File

@@ -64,14 +64,4 @@
</div>
<% end %>
<% items = local_assigns[:indexa_capital_items] || @indexa_capital_items || Current.family.indexa_capital_items.where.not(username: [nil, ""], document: [nil, ""], password: [nil, ""]).or(Current.family.indexa_capital_items.where.not(api_token: [nil, ""])) %>
<div class="flex items-center gap-2">
<% if items&.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary"><%= t("indexa_capital_items.panel.status_configured_html", accounts_path: accounts_path).html_safe %></p>
<% else %>
<div class="w-2 h-2 bg-gray-400 rounded-full"></div>
<p class="text-sm text-secondary"><%= t("indexa_capital_items.panel.status_not_configured") %></p>
<% end %>
</div>
</div>

View File

@@ -44,19 +44,9 @@
value: lunchflow_item.base_url %>
<div class="flex justify-end">
<%= form.submit is_new_record ? "Save Configuration" : "Update Configuration",
<%= form.submit is_new_record ? "Save and connect" : "Update connection",
class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors" %>
</div>
<% end %>
<% items = local_assigns[:lunchflow_items] || @lunchflow_items || Current.family.lunchflow_items.where.not(api_key: nil) %>
<div class="flex items-center gap-2">
<% if items&.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary">Configured and ready to use. Visit the <a href="<%= accounts_path %>" class="link">Accounts</a> tab to manage and set up accounts.</p>
<% else %>
<div class="w-2 h-2 bg-gray-400 rounded-full"></div>
<p class="text-sm text-secondary">Not configured</p>
<% end %>
</div>
</div>

View File

@@ -130,13 +130,4 @@
<% end %>
</details>
<div class="flex items-center gap-2">
<% if credentialed_items.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary"><%= t("mercury_items.provider_panel.configured_html", accounts_link: link_to(t("mercury_items.provider_panel.accounts_link"), accounts_path, class: "link")) %></p>
<% else %>
<div class="w-2 h-2 bg-gray-400 rounded-full"></div>
<p class="text-sm text-secondary"><%= t("mercury_items.provider_panel.not_configured") %></p>
<% end %>
</div>
</div>

View File

@@ -67,20 +67,10 @@
<% end %>
<div class="flex justify-end">
<%= form.submit "Save Configuration",
<%= form.submit "Save and connect",
class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors" %>
</div>
</div>
<% end %>
<%# Show configuration status %>
<div class="flex items-center gap-2 mt-4">
<% if configuration.configured? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary">Configured and ready to use</p>
<% else %>
<div class="w-2 h-2 bg-gray-400 rounded-full"></div>
<p class="text-sm text-secondary">Not configured</p>
<% end %>
</div>
</div>

View File

@@ -31,18 +31,9 @@
type: :password %>
<div class="flex justify-end">
<%= form.submit "Save Configuration",
<%= form.submit "Save and connect",
class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors" %>
</div>
<% end %>
<div class="flex items-center gap-2">
<% if @simplefin_items&.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary">Configured and ready to use. Visit the <a href="<%= accounts_path %>" class="link">Accounts</a> tab to manage and set up accounts.</p>
<% else %>
<div class="w-2 h-2 bg-gray-400 rounded-full"></div>
<p class="text-sm text-secondary">Not configured</p>
<% end %>
</div>
</div>

View File

@@ -51,56 +51,43 @@
<% items = local_assigns[:snaptrade_items] || @snaptrade_items || Current.family.snaptrade_items.where.not(client_id: [nil, ""]) %>
<div class="border-t border-primary pt-4 mt-4">
<% if items&.any? %>
<% item = items.first %>
<% if item.user_registered? %>
<details class="group"
data-controller="lazy-load"
data-action="toggle->lazy-load#toggled"
data-lazy-load-url-value="<%= connections_snaptrade_item_path(item) %>"
data-lazy-load-auto-open-param-value="manage">
<summary class="flex items-center justify-between cursor-pointer list-none [&::-webkit-details-marker]:hidden">
<div class="flex items-center gap-2">
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary">
<%= t("providers.snaptrade.status_connected", count: item.snaptrade_accounts.count) %>
<% if item.unlinked_accounts_count > 0 %>
<span class="text-warning">(<%= t("providers.snaptrade.needs_setup", count: item.unlinked_accounts_count) %>)</span>
<% end %>
</p>
</div>
<span class="flex items-center gap-1 text-sm text-secondary hover:text-primary">
<%= t("providers.snaptrade.manage_connections") %>
<%= icon "chevron-right", class: "w-3 h-3 transition-transform group-open:rotate-90" %>
</span>
</summary>
<div class="mt-3 space-y-3" data-lazy-load-target="content">
<p class="text-xs text-secondary">
<%= t("providers.snaptrade.connection_limit_info") %>
<% if items&.any? && items.first.user_registered? %>
<% item = items.first %>
<div class="border-t border-primary pt-4 mt-4">
<details class="group"
data-controller="lazy-load"
data-action="toggle->lazy-load#toggled"
data-lazy-load-url-value="<%= connections_snaptrade_item_path(item) %>"
data-lazy-load-auto-open-param-value="manage">
<summary class="flex items-center justify-between cursor-pointer list-none [&::-webkit-details-marker]:hidden">
<div class="flex items-center gap-2">
<p class="text-sm text-secondary">
<%= t("providers.snaptrade.status_connected", count: item.snaptrade_accounts.count) %>
<% if item.unlinked_accounts_count > 0 %>
<span class="text-warning">(<%= t("providers.snaptrade.needs_setup", count: item.unlinked_accounts_count) %>)</span>
<% end %>
</p>
<div data-lazy-load-target="loading" class="flex items-center gap-2 text-sm text-secondary py-2">
<%= icon "loader-2", class: "w-4 h-4 animate-spin" %>
<%= t("providers.snaptrade.loading_connections") %>
</div>
<div data-lazy-load-target="frame">
</div>
</div>
</details>
<% else %>
<div class="flex items-center gap-2">
<div class="w-2 h-2 bg-warning rounded-full"></div>
<p class="text-sm text-secondary"><%= t("providers.snaptrade.status_needs_registration") %></p>
<span class="flex items-center gap-1 text-sm text-secondary hover:text-primary">
<%= t("providers.snaptrade.manage_connections") %>
<%= icon "chevron-right", class: "w-3 h-3 transition-transform group-open:rotate-90" %>
</span>
</summary>
<div class="mt-3 space-y-3" data-lazy-load-target="content">
<p class="text-xs text-secondary">
<%= t("providers.snaptrade.connection_limit_info") %>
</p>
<div data-lazy-load-target="loading" class="flex items-center gap-2 text-sm text-secondary py-2">
<%= icon "loader-2", class: "w-4 h-4 animate-spin" %>
<%= t("providers.snaptrade.loading_connections") %>
</div>
<div data-lazy-load-target="frame">
</div>
</div>
<% end %>
<% else %>
<div class="flex items-center gap-2">
<div class="w-2 h-2 bg-gray-400 rounded-full"></div>
<p class="text-sm text-secondary"><%= t("providers.snaptrade.status_not_configured") %></p>
</div>
<% end %>
</div>
</details>
</div>
<% end %>
</div>

View File

@@ -56,13 +56,4 @@
</div>
<% end %>
<div class="flex items-center gap-2">
<% if Current.family.sophtron_items.any? %>
<div class="w-2 h-2 bg-success rounded-full"></div>
<p class="text-sm text-secondary"><%= t("sophtron_items.sophtron_panel.status.configured_html", accounts_path: accounts_path) %></p>
<% else %>
<div class="w-2 h-2 bg-muted rounded-full"></div>
<p class="text-sm text-secondary"><%= t("sophtron_items.sophtron_panel.status.not_configured") %></p>
<% end %>
</div>
</div>
</div>

View File

@@ -1,5 +1,8 @@
<%= render DS::Dialog.new(frame: "drawer", responsive: true, auto_open: true) do |dialog| %>
<% dialog.with_header(title: @panel_title) %>
<% provider_key = @panel_key || @provider_configuration&.provider_key&.to_s %>
<% dialog.with_header(custom_header: true) do %>
<%= render "settings/providers/drawer_header", provider_key: provider_key, title: @panel_title %>
<% end %>
<% dialog.with_body do %>
<% if @panel_partial %>
<turbo-frame id="<%= @panel_key %>-connect-form" target="_top">
@@ -10,5 +13,9 @@
<%= render "settings/providers/provider_form", configuration: @provider_configuration %>
</turbo-frame>
<% end %>
<p class="text-xs text-secondary mt-6 pt-3 border-t border-secondary/10">
<%= t("settings.providers.drawer_trust_statement") %>
</p>
<% end %>
<% end %>

View File

@@ -198,6 +198,7 @@ en:
maturity:
beta: Beta
alpha: Alpha
drawer_trust_statement: "Read-only — Sure can never move money. Stored encrypted."
connect: Connect
groups:
your_connections: Your connections