diff --git a/app/components/settings/provider_card.rb b/app/components/settings/provider_card.rb
index c57657a2e..e96a69336 100644
--- a/app/components/settings/provider_card.rb
+++ b/app/components/settings/provider_card.rb
@@ -10,7 +10,7 @@ class Settings::ProviderCard < ApplicationComponent
end
def initialize(provider_key:, name:, tagline: nil, region: nil, kind: nil, tier: nil,
- maturity: :stable, logo_bg: "bg-gray-500", logo_text: nil)
+ maturity: :stable, logo_text: nil)
@provider_key = provider_key
@name = name
@tagline = tagline
@@ -18,11 +18,10 @@ class Settings::ProviderCard < ApplicationComponent
@kind = kind
@tier = tier
@maturity = maturity.to_sym
- @logo_bg = logo_bg
@logo_text = logo_text || name.first(2).upcase
end
- attr_reader :name, :tagline, :logo_bg, :logo_text
+ attr_reader :name, :tagline, :logo_text
def maturity_label
self.class.maturity_label(@maturity)
diff --git a/app/javascript/controllers/providers_filter_controller.js b/app/javascript/controllers/providers_filter_controller.js
index e7134de8c..54004b2f6 100644
--- a/app/javascript/controllers/providers_filter_controller.js
+++ b/app/javascript/controllers/providers_filter_controller.js
@@ -2,8 +2,10 @@ import { Controller } from "@hotwired/stimulus";
// Connects to data-controller="providers-filter"
// Filters provider cards by free-text query and a chip-selected kind.
+// Updates the visible-count target on the section heading and toggles
+// an empty-state target when no card matches.
export default class extends Controller {
- static targets = ["input", "chip", "card", "empty"];
+ static targets = ["input", "chip", "card", "empty", "count"];
static values = { kind: { type: String, default: "all" } };
connect() {
@@ -29,6 +31,10 @@ export default class extends Controller {
if (visible) visibleCount++;
});
+ if (this.hasCountTarget) {
+ this.countTarget.textContent = visibleCount;
+ }
+
if (this.hasEmptyTarget) {
this.emptyTarget.classList.toggle("hidden", visibleCount > 0);
}
@@ -40,6 +46,14 @@ export default class extends Controller {
this.filter();
}
+ clear() {
+ if (this.hasInputTarget) this.inputTarget.value = "";
+ this.kindValue = "all";
+ this.syncChipState();
+ this.filter();
+ if (this.hasInputTarget) this.inputTarget.focus();
+ }
+
syncChipState() {
if (!this.hasChipTarget) return;
this.chipTargets.forEach((chip) => {
diff --git a/app/models/provider/metadata.rb b/app/models/provider/metadata.rb
index 702e0f4ee..dfdb48be7 100644
--- a/app/models/provider/metadata.rb
+++ b/app/models/provider/metadata.rb
@@ -1,97 +1,22 @@
class Provider
module Metadata
REGISTRY = {
- simplefin: {
- region: "US",
- kind: "Bank",
- maturity: :stable,
- logo_bg: "bg-blue-600",
- logo_text: "SF"
- },
- lunchflow: {
- region: "US",
- kind: "Bank",
- maturity: :stable,
- logo_bg: "bg-orange-500",
- logo_text: "LF"
- },
- enable_banking: {
- region: "EU",
- kind: "Bank",
- maturity: :beta,
- logo_bg: "bg-purple-600",
- logo_text: "EB"
- },
- coinstats: {
- region: "Global",
- kind: "Crypto",
- maturity: :beta,
- logo_bg: "bg-yellow-500",
- logo_text: "CS"
- },
- mercury: {
- region: "US",
- kind: "Bank",
- maturity: :beta,
- logo_bg: "bg-cyan-600",
- logo_text: "ME"
- },
- coinbase: {
- region: "Global",
- kind: "Crypto",
- maturity: :beta,
- logo_bg: "bg-blue-500",
- logo_text: "CB"
- },
- binance: {
- region: "Global",
- kind: "Crypto",
- maturity: :beta,
- logo_bg: "bg-yellow-400",
- logo_text: "BI"
- },
- snaptrade: {
- region: "US / CA",
- kind: "Investment",
- maturity: :beta,
- logo_bg: "bg-green-600",
- logo_text: "ST"
- },
- indexa_capital: {
- region: "ES",
- kind: "Investment",
- maturity: :alpha,
- logo_bg: "bg-red-600",
- logo_text: "IC"
- },
- sophtron: {
- region: "US",
- kind: "Bank",
- maturity: :alpha,
- logo_bg: "bg-teal-600",
- logo_text: "SO"
- },
- plaid: {
- region: "US",
- kind: "Bank",
- tier: "Paid",
- maturity: :stable,
- logo_bg: "bg-indigo-600",
- logo_text: "PL"
- },
- plaid_eu: {
- name: "Plaid EU",
- region: "EU",
- kind: "Bank",
- tier: "Paid",
- maturity: :stable,
- logo_bg: "bg-indigo-600",
- logo_text: "PL"
- }
+ simplefin: { region: "US", kind: "Bank", maturity: :stable, logo_text: "SF" },
+ lunchflow: { region: "US", kind: "Bank", maturity: :stable, logo_text: "LF" },
+ enable_banking: { region: "EU", kind: "Bank", maturity: :beta, logo_text: "EB" },
+ coinstats: { region: "Global", kind: "Crypto", maturity: :beta, logo_text: "CS" },
+ mercury: { region: "US", kind: "Bank", maturity: :beta, logo_text: "ME" },
+ coinbase: { region: "Global", kind: "Crypto", maturity: :beta, logo_text: "CB" },
+ binance: { region: "Global", kind: "Crypto", maturity: :beta, logo_text: "BI" },
+ snaptrade: { region: "US / CA", kind: "Investment", maturity: :beta, logo_text: "ST" },
+ indexa_capital: { region: "ES", kind: "Investment", maturity: :alpha, logo_text: "IC" },
+ sophtron: { region: "US", kind: "Bank", maturity: :alpha, logo_text: "SO" },
+ plaid: { region: "US", kind: "Bank", tier: "Paid", maturity: :stable, logo_text: "PL" },
+ plaid_eu: { name: "Plaid EU", region: "EU", kind: "Bank", tier: "Paid", maturity: :stable, logo_text: "PL" }
}.freeze
def self.for(provider_key)
- REGISTRY[provider_key.to_sym] || { logo_text: provider_key.to_s.first(2).upcase, logo_bg: "bg-gray-500" }
+ REGISTRY[provider_key.to_sym] || { logo_text: provider_key.to_s.first(2).upcase }
end
end
end
diff --git a/app/views/settings/providers/_binance_panel.html.erb b/app/views/settings/providers/_binance_panel.html.erb
index 2ad0158e2..f555ff912 100644
--- a/app/views/settings/providers/_binance_panel.html.erb
+++ b/app/views/settings/providers/_binance_panel.html.erb
@@ -8,7 +8,11 @@
<%= t("settings.providers.binance_panel.step2") %>
<%= t("settings.providers.binance_panel.step3") %>
-
<%= t("settings.providers.binance_panel.no_withdraw_warning") %>
+
diff --git a/app/views/settings/providers/_drawer_header.html.erb b/app/views/settings/providers/_drawer_header.html.erb
index df439dafd..9cb044843 100644
--- a/app/views/settings/providers/_drawer_header.html.erb
+++ b/app/views/settings/providers/_drawer_header.html.erb
@@ -3,9 +3,9 @@
<% maturity_label = meta ? Settings::ProviderCard.maturity_label(meta[:maturity]) : nil %>
- <% if meta && meta[:logo_bg].present? %>
-
- <%= meta[:logo_text] %>
+ <% if meta && meta[:logo_text].present? %>
+
+ <%= meta[:logo_text] %>
<% end %>
<%= title %>
diff --git a/app/views/settings/providers/_group_heading.html.erb b/app/views/settings/providers/_group_heading.html.erb
index 970be0443..e0a831f49 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-5 mb-1.5 px-1" do %>
+<%= tag.div id: anchor.presence, class: "flex items-baseline justify-between gap-3 mt-6 mb-1.5 px-1" do %>
<%= title %>
<% if count %>
diff --git a/app/views/settings/providers/_search_filters.html.erb b/app/views/settings/providers/_search_filters.html.erb
index 4d79586cb..f4c295faf 100644
--- a/app/views/settings/providers/_search_filters.html.erb
+++ b/app/views/settings/providers/_search_filters.html.erb
@@ -1,4 +1,3 @@
-<%# locals: (count:) %>
"
- placeholder="<%= t("settings.providers.search_filters.placeholder", count: count) %>"
+ placeholder="<%= t("settings.providers.search_filters.placeholder") %>"
class="block w-full border border-secondary rounded-md py-2.5 pl-10 pr-3 bg-container focus:ring-gray-500 sm:text-sm">
<%= icon "search", class: "text-secondary" %>
diff --git a/app/views/settings/providers/_snaptrade_panel.html.erb b/app/views/settings/providers/_snaptrade_panel.html.erb
index 8475eee91..fdacf3424 100644
--- a/app/views/settings/providers/_snaptrade_panel.html.erb
+++ b/app/views/settings/providers/_snaptrade_panel.html.erb
@@ -10,7 +10,7 @@
<%= t("providers.snaptrade.step_4") %>
-
<%= icon("alert-triangle", class: "inline-block w-4 h-4 mr-1") %><%= t("providers.snaptrade.free_tier_warning") %>
+
<%= icon("alert-triangle", size: "sm", class: "inline-block mr-1") %><%= t("providers.snaptrade.free_tier_warning") %>
<% error_msg = local_assigns[:error_message] || @error_message %>
diff --git a/app/views/settings/providers/show.html.erb b/app/views/settings/providers/show.html.erb
index 42ccdae66..11d085887 100644
--- a/app/views/settings/providers/show.html.erb
+++ b/app/views/settings/providers/show.html.erb
@@ -46,16 +46,23 @@
<% if @available.any? %>
- <%= render "settings/providers/search_filters", count: @available.size %>
+ <%= render "settings/providers/search_filters" %>
- <%= render "settings/providers/group_heading",
- title: t("settings.providers.groups.available"),
- count: @available.size,
- anchor: "available" %>
+
+
+ <%= t("settings.providers.groups.available") %>
+
+ ยท <%= @available.size %>
+
+
+
-
- <%= t("settings.providers.empty_filter") %>
-
+
+ <%= t("settings.providers.empty_filter") %>
+
+ <%= t("settings.providers.clear_filter") %>
+
+
<% @available.each do |entry| %>
@@ -68,7 +75,6 @@
kind: meta[:kind],
tier: meta[:tier],
maturity: meta[:maturity] || :stable,
- logo_bg: meta[:logo_bg],
logo_text: meta[:logo_text]
) %>
<% end %>
diff --git a/config/locales/views/settings/en.yml b/config/locales/views/settings/en.yml
index b6b26fb4d..1d424ef3f 100644
--- a/config/locales/views/settings/en.yml
+++ b/config/locales/views/settings/en.yml
@@ -234,15 +234,14 @@ en:
plaid_eu: Connect European financial institutions via Plaid (PSD2 / Open Banking).
search_filters:
aria_label: Search providers
- placeholder:
- one: "Search %{count} provider"
- other: "Search %{count} providers"
+ placeholder: Search providers
chips:
all: All
bank: Banks
crypto: Crypto
- investment: Investment
+ investment: Investments
empty_filter: No providers match your filter.
+ clear_filter: Clear filters
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.
@@ -265,7 +264,7 @@ en:
step2: "Create a new API key with Enable Reading permission only"
step3: "Paste your API Key and Secret below"
no_withdraw_warning: "Warning: do NOT enable withdrawal permissions"
- ip_hint_title: "IP Whitelisting Required"
+ ip_hint_title: "IP whitelisting required"
ip_hint_body: "Add the app server's egress IP to the Binance API Key whitelist:"
ip_hint_contact_admin: "Contact your administrator to obtain the app server's egress IP address."
api_key_label: API Key