From e0aab867d63b3d76ceb9fd7078778b27990dbea8 Mon Sep 17 00:00:00 2001 From: Guillem Arias Date: Sat, 9 May 2026 12:29:34 +0200 Subject: [PATCH] refactor(settings/providers): tighten paddings, dedupe maturity badge, semantic + a11y fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pixel-level alignment to the design's §05 mock + cleanup from a DS audit pass. Paddings, margins, font sizes - Health strip: my-4 → mt-4 mb-5 to match the design's 16px / 20px vertical breathing room. - Search filters bar: gap-2 → gap-2.5; mt-2 → mt-5 mb-3 (was missing the 12px bottom margin entirely). - Search box: rounded-lg → rounded-[10px]; px-3 py-2 → px-[14px] py-[9px]. Search icon downsized w-4 → w-3.5 to match. - Chip group: p-1 → p-[3px]; rounded-lg → rounded-[10px]. - Chip: py-1 → py-[5px]; rounded-md → rounded-lg. - Group heading: mt-2 → mt-[18px]; mb-1 → mb-1.5. - Status pill: text-xs → text-[11px]. - Provider card: gap-3 → gap-2.5 (outer + top); name gets explicit text-sm; tagline + foot 14px → 13px; arrow icon w-4 → w-3.5. - Sync icon button: p-1 → fixed w-7 h-7 (28×28) so the row hit target matches the design's column width. - Connect drawer header logo glyph: text-[10px] → text-xs (matches the available card's logo-glyph treatment). Component / partial cleanup (DS audit follow-ups) - New _maturity_badge partial replaces the inline span that was duplicated in 3 places (_connection_row, _drawer_header, provider_card.html.erb). - Settings::ProviderCard.maturity_label class method centralizes the MATURITY_LABELS lookup; callers no longer reach into the constant. - _connection_row title:

(the row sits inside the "Your connections" h2 group heading; nested h2s flattened the outline). - show.html.erb encryption error:

for the same reason. Locale - Drop orphaned keys: settings.providers.groups.connected and groups.needs_attention (no view code uses them) plus the leftover show.coinbase_title block. - Health strip "needs reconsent" → "needs attention" so the strip copy lines up with the per-row status pill ("Action needed") and the original group heading wording. A11y - focus-visible:ring-2 on chip buttons, provider-card link, and focus-within:ring-2 on the search input wrapper. Keyboard users now get a visible focus state. - Search input: explicit autocomplete="off" (erb_lint hint). --- app/components/settings/provider_card.html.erb | 16 +++++++--------- app/components/settings/provider_card.rb | 5 +++++ .../settings/providers/_connection_row.html.erb | 9 +++------ .../settings/providers/_drawer_header.html.erb | 9 +++------ .../settings/providers/_group_heading.html.erb | 2 +- .../settings/providers/_health_strip.html.erb | 2 +- .../settings/providers/_maturity_badge.html.erb | 4 ++++ .../settings/providers/_search_filters.html.erb | 11 ++++++----- .../settings/providers/_status_pill.html.erb | 2 +- .../settings/providers/_sync_button.html.erb | 2 +- app/views/settings/providers/show.html.erb | 2 +- config/locales/views/settings/en.yml | 6 +----- test/system/settings/providers_test.rb | 2 +- 13 files changed, 35 insertions(+), 37 deletions(-) create mode 100644 app/views/settings/providers/_maturity_badge.html.erb diff --git a/app/components/settings/provider_card.html.erb b/app/components/settings/provider_card.html.erb index b3ba5c29c..5d0ca28f9 100644 --- a/app/components/settings/provider_card.html.erb +++ b/app/components/settings/provider_card.html.erb @@ -1,16 +1,14 @@ <%= link_to connect_path, - class: "bg-container shadow-border-xs rounded-xl p-4 flex flex-col gap-3 text-primary hover:bg-container-hover transition-colors", + class: "bg-container shadow-border-xs rounded-xl p-4 flex flex-col gap-2.5 text-primary hover:bg-container-hover transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-alpha-black-100", data: { turbo_frame: "drawer", turbo_prefetch: "false" }.merge(filter_data) do %> -
+
<%= logo_text %>
- <%= name %> - <% if maturity_label %> - <%= maturity_label %> - <% end %> + <%= name %> + <%= render "settings/providers/maturity_badge", label: maturity_label %>
<% if meta_line.present? %>

<%= meta_line %>

@@ -18,10 +16,10 @@
<% if tagline.present? %> -

<%= tagline %>

+

<%= tagline %>

<% end %> -
+
<%= t("settings.providers.connect") %> - <%= helpers.icon "arrow-right", class: "w-4 h-4" %> + <%= helpers.icon "arrow-right", class: "w-3.5 h-3.5" %>
<% end %> diff --git a/app/components/settings/provider_card.rb b/app/components/settings/provider_card.rb index 50ec38fd2..ddd6b2bf8 100644 --- a/app/components/settings/provider_card.rb +++ b/app/components/settings/provider_card.rb @@ -4,6 +4,11 @@ class Settings::ProviderCard < ApplicationComponent alpha: "settings.providers.maturity.alpha" }.freeze + def self.maturity_label(maturity) + key = MATURITY_LABELS[maturity&.to_sym] + I18n.t(key) if key + end + def initialize(provider_key:, name:, tagline: nil, region: nil, kind: nil, tier: nil, maturity: :stable, logo_bg: "bg-gray-500", logo_text: nil) @provider_key = provider_key diff --git a/app/views/settings/providers/_connection_row.html.erb b/app/views/settings/providers/_connection_row.html.erb index 61f4098f6..0991d514f 100644 --- a/app/views/settings/providers/_connection_row.html.erb +++ b/app/views/settings/providers/_connection_row.html.erb @@ -11,8 +11,7 @@ end sync_action = entry[:partial].present? ? render("settings/providers/sync_button", provider_key: entry[:provider_key], last_synced_at: last_synced) : nil status_pill = render("settings/providers/status_pill", status: status) - maturity_key = Settings::ProviderCard::MATURITY_LABELS[entry[:maturity]] - maturity_lbl = maturity_key ? t(maturity_key) : nil + maturity_lbl = Settings::ProviderCard.maturity_label(entry[:maturity]) %>
class="group bg-container shadow-border-xs rounded-xl <%= border_class %>" @@ -20,10 +19,8 @@ <%= icon "chevron-right", class: "w-3.5 h-3.5 text-secondary group-open:rotate-90 transition-transform shrink-0" %>
-

<%= entry[:title] %>

- <% if maturity_lbl %> - <%= maturity_lbl %> - <% end %> +

<%= entry[:title] %>

+ <%= render "settings/providers/maturity_badge", label: maturity_lbl %>
<% if meta.present? %> diff --git a/app/views/settings/providers/_drawer_header.html.erb b/app/views/settings/providers/_drawer_header.html.erb index 2d3c12b43..df439dafd 100644 --- a/app/views/settings/providers/_drawer_header.html.erb +++ b/app/views/settings/providers/_drawer_header.html.erb @@ -1,18 +1,15 @@ <%# 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 %> +<% maturity_label = meta ? Settings::ProviderCard.maturity_label(meta[:maturity]) : nil %>
<% if meta && meta[:logo_bg].present? %> - <%= meta[:logo_text] %> + <%= meta[:logo_text] %> <% end %>

<%= title %>

- <% if maturity_label %> - <%= maturity_label %> - <% end %> + <%= render "settings/providers/maturity_badge", label: maturity_label %>
<%= render DS::Button.new( variant: "icon", diff --git a/app/views/settings/providers/_group_heading.html.erb b/app/views/settings/providers/_group_heading.html.erb index e7246c4c2..840d7903a 100644 --- a/app/views/settings/providers/_group_heading.html.erb +++ b/app/views/settings/providers/_group_heading.html.erb @@ -1,5 +1,5 @@ <%# locals: (title:, count: nil, description: nil, anchor: nil) %> -<%= tag.div id: anchor.presence, class: "flex items-baseline justify-between gap-3 mt-2 mb-1 px-1" do %> +<%= tag.div id: anchor.presence, class: "flex items-baseline justify-between gap-3 mt-[18px] mb-1.5 px-1" do %>

<%= title %> <% if count %> diff --git a/app/views/settings/providers/_health_strip.html.erb b/app/views/settings/providers/_health_strip.html.erb index 9db195fee..05af86342 100644 --- a/app/views/settings/providers/_health_strip.html.erb +++ b/app/views/settings/providers/_health_strip.html.erb @@ -1,5 +1,5 @@ <%# locals: (connected:, needs_attention:, accounts_syncing:, last_synced_at:) %> -
+
<%= icon "check", class: "w-3.5 h-3.5 text-success" %> <%= connected %> diff --git a/app/views/settings/providers/_maturity_badge.html.erb b/app/views/settings/providers/_maturity_badge.html.erb new file mode 100644 index 000000000..69e58887c --- /dev/null +++ b/app/views/settings/providers/_maturity_badge.html.erb @@ -0,0 +1,4 @@ +<%# locals: (label:) %> +<% if label %> + <%= label %> +<% end %> diff --git a/app/views/settings/providers/_search_filters.html.erb b/app/views/settings/providers/_search_filters.html.erb index 57c94efdf..16c71a165 100644 --- a/app/views/settings/providers/_search_filters.html.erb +++ b/app/views/settings/providers/_search_filters.html.erb @@ -1,15 +1,16 @@ <%# locals: (count:) %> -
-
- <%= icon "search", class: "w-4 h-4 text-secondary shrink-0" %> +
+
+ <%= icon "search", class: "w-3.5 h-3.5 text-secondary shrink-0" %> " placeholder="<%= t("settings.providers.search_filters.placeholder", count: count) %>" class="bg-transparent border-0 outline-none flex-1 text-sm text-primary placeholder:text-subdued">
-
+
<% %w[all bank crypto investment].each do |kind| %> <% active = kind == "all" %> <% end %> diff --git a/app/views/settings/providers/_status_pill.html.erb b/app/views/settings/providers/_status_pill.html.erb index 1f46b9216..389af3d7a 100644 --- a/app/views/settings/providers/_status_pill.html.erb +++ b/app/views/settings/providers/_status_pill.html.erb @@ -1,7 +1,7 @@ <%# locals: (status:) %> <% classes = status_pill_classes(status) %> <% dot_class, pill_class = classes[:dot], classes[:pill] %> - + <%= t("settings.providers.status.#{status}") %> diff --git a/app/views/settings/providers/_sync_button.html.erb b/app/views/settings/providers/_sync_button.html.erb index ba97b2ce1..697b04ba8 100644 --- a/app/views/settings/providers/_sync_button.html.erb +++ b/app/views/settings/providers/_sync_button.html.erb @@ -6,7 +6,7 @@ disabled: recently_synced, title: button_label, aria: { label: button_label }, - class: "inline-flex items-center p-1 rounded text-secondary hover:text-primary hover:bg-alpha-black-50 transition-colors disabled:opacity-40 disabled:cursor-not-allowed", + class: "inline-flex items-center justify-center w-7 h-7 rounded-md text-secondary hover:text-primary hover:bg-alpha-black-50 transition-colors disabled:opacity-40 disabled:cursor-not-allowed", form: { onclick: "event.stopPropagation()", class: "inline-flex" } do %> <%= icon "refresh-cw", class: "w-3.5 h-3.5" %> <% end %> diff --git a/app/views/settings/providers/show.html.erb b/app/views/settings/providers/show.html.erb index e8356f03e..fae17973c 100644 --- a/app/views/settings/providers/show.html.erb +++ b/app/views/settings/providers/show.html.erb @@ -6,7 +6,7 @@
<%= icon("triangle-alert", class: "w-5 h-5 text-destructive shrink-0 mt-0.5") %>
-

<%= t("settings.providers.encryption_error.title") %>

+

<%= t("settings.providers.encryption_error.title") %>

<%= t("settings.providers.encryption_error.message") %>

diff --git a/config/locales/views/settings/en.yml b/config/locales/views/settings/en.yml index 256580d72..33b0b87fd 100644 --- a/config/locales/views/settings/en.yml +++ b/config/locales/views/settings/en.yml @@ -202,13 +202,11 @@ en: connect: Connect groups: your_connections: Your connections - connected: Connected - needs_attention: Action needed available: Available empty_available: All available providers are connected. health_strip: connected: connected - needs_attention: needs reconsent + needs_attention: needs attention accounts_syncing: accounts syncing last_synced: Last synced %{time} ago meta: @@ -249,8 +247,6 @@ en: crypto: Crypto investment: Investment empty_filter: No providers match your filter. - show: - coinbase_title: Coinbase encryption_error: title: Encryption Configuration Required message: Active Record encryption keys are not configured. Please ensure the encryption credentials (active_record_encryption.primary_key, active_record_encryption.deterministic_key, and active_record_encryption.key_derivation_salt) are properly set up in your Rails credentials or environment variables before using sync providers. diff --git a/test/system/settings/providers_test.rb b/test/system/settings/providers_test.rb index 76148e12d..c60d3b019 100644 --- a/test/system/settings/providers_test.rb +++ b/test/system/settings/providers_test.rb @@ -33,7 +33,7 @@ class Settings::ProvidersTest < ApplicationSystemTestCase visit settings_providers_path - titles = all("details").map { |d| d.find("summary h2", match: :first).text.squish } + titles = all("details").map { |d| d.find("summary h3", match: :first).text.squish } assert_equal titles.sort_by(&:downcase), titles, "Connection panels should render alphabetically by title" connections_heading = page.find(:xpath, "//h2[contains(normalize-space(), 'Your connections')]")