mirror of
https://github.com/we-promise/sure.git
synced 2026-05-28 06:54:56 +00:00
Per the design review, the "Add another provider · Browse providers" card was a redirect to content one scroll-tick away. A search input plus kind chips lets users self-segment the catalog and is the right tool once it grows beyond the four to twelve providers we ship today. - New providers_filter Stimulus controller — case-insensitive free text search across name/region/kind, plus a chip group with All / Banks / Crypto / Investment that toggle visibility via Tailwind's `hidden` class. - _search_filters partial: search box (count-pluralized placeholder) + chip group, ARIA-labelled and aria-pressed for the chips. - ProviderCard exposes filter_data (target + name/region/kind data attrs) so the controller can match without re-rendering. - Lunchflow's `kind` was "Lunch" — switched to "Bank" so it falls under the Banks chip alongside its actual offering (it aggregates banks). - Drops the add_provider_cta partial and its locale entries; adds search_filters.* and an empty_filter message.
102 lines
5.0 KiB
Plaintext
102 lines
5.0 KiB
Plaintext
<%= content_for :page_title, t("settings.providers.bank_sync.page_title") %>
|
|
|
|
<div class="space-y-4">
|
|
<% if @encryption_error %>
|
|
<div class="p-4 rounded-lg bg-destructive/10 border border-destructive/20">
|
|
<div class="flex items-start gap-3">
|
|
<%= icon("triangle-alert", class: "w-5 h-5 text-destructive shrink-0 mt-0.5") %>
|
|
<div>
|
|
<h3 class="font-medium text-primary"><%= t("settings.providers.encryption_error.title") %></h3>
|
|
<p class="text-secondary text-sm mt-1"><%= t("settings.providers.encryption_error.message") %></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% else %>
|
|
<div class="flex items-start justify-between gap-4">
|
|
<p class="text-secondary"><%= t("settings.providers.bank_sync.lede") %></p>
|
|
<% if @connected.any? || @needs_attention.any? %>
|
|
<% sync_all_disabled = Current.family.last_sync_all_attempted_at.present? && Current.family.last_sync_all_attempted_at > 30.seconds.ago %>
|
|
<%= button_to sync_all_settings_providers_path,
|
|
method: :post,
|
|
disabled: sync_all_disabled,
|
|
title: sync_all_disabled ? t("settings.providers.sync_all_recently") : nil,
|
|
class: "inline-flex items-center gap-2 shrink-0 whitespace-nowrap px-3 py-1.5 text-sm font-medium text-secondary hover:text-primary border border-secondary rounded-lg hover:border-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed" do %>
|
|
<%= icon "refresh-cw", class: "w-4 h-4" %>
|
|
<%= t("settings.providers.sync_all") %>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
|
|
<% all_connections = @needs_attention + @connected %>
|
|
|
|
<% if all_connections.any? %>
|
|
<%= render "settings/providers/group_heading",
|
|
title: t("settings.providers.groups.your_connections"),
|
|
count: all_connections.size %>
|
|
<% end %>
|
|
|
|
<% all_connections.each do |entry| %>
|
|
<% auto_open = [ :warn, :err ].include?(entry[:summary][:status]) || all_connections.size == 1 %>
|
|
<% sync_action = entry[:partial].present? ? render("settings/providers/sync_button", provider_key: entry[:provider_key], last_synced_at: entry[:summary][:last_synced_at]) : nil %>
|
|
<% maturity_label_key = Settings::ProviderCard::MATURITY_LABELS[entry[:maturity]] %>
|
|
<% maturity_label = maturity_label_key ? t(maturity_label_key) : nil %>
|
|
<% maturity_badge = maturity_label ? content_tag(:span, maturity_label, class: "text-xs font-medium px-1.5 py-0.5 rounded-full bg-alpha-black-50 text-secondary") : nil %>
|
|
<% status_pill = render("settings/providers/status_pill", status: entry[:summary][:status]) %>
|
|
<%= settings_section title: entry[:title],
|
|
collapsible: true,
|
|
open: auto_open,
|
|
auto_open_param: entry[:auto_open_param],
|
|
status: status_pill,
|
|
meta: entry[:summary][:meta],
|
|
actions: sync_action,
|
|
badge: maturity_badge do %>
|
|
<% if entry[:configuration] %>
|
|
<%= render "settings/providers/provider_form", configuration: entry[:configuration] %>
|
|
<% else %>
|
|
<turbo-frame id="<%= entry[:turbo_id] %>-providers-panel">
|
|
<%= render "settings/providers/#{entry[:partial]}" %>
|
|
</turbo-frame>
|
|
<% end %>
|
|
<% end %>
|
|
<% end %>
|
|
|
|
<% if @available.any? %>
|
|
<div data-controller="providers-filter">
|
|
<%= render "settings/providers/search_filters", count: @available.size %>
|
|
|
|
<%= render "settings/providers/group_heading",
|
|
title: t("settings.providers.groups.available"),
|
|
count: @available.size,
|
|
anchor: "available" %>
|
|
|
|
<p data-providers-filter-target="empty" class="hidden text-sm text-secondary px-1 py-2">
|
|
<%= t("settings.providers.empty_filter") %>
|
|
</p>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
<% @available.each do |entry| %>
|
|
<% meta = Provider::Metadata.for(entry[:provider_key]) %>
|
|
<%= render Settings::ProviderCard.new(
|
|
provider_key: entry[:provider_key],
|
|
name: entry[:title],
|
|
tagline: t("settings.providers.taglines.#{entry[:provider_key]}", default: nil),
|
|
region: meta[:region],
|
|
kind: meta[:kind],
|
|
tier: meta[:tier],
|
|
maturity: meta[:maturity] || :stable,
|
|
logo_bg: meta[:logo_bg],
|
|
logo_text: meta[:logo_text]
|
|
) %>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
<% else %>
|
|
<%= render "settings/providers/group_heading",
|
|
title: t("settings.providers.groups.available"),
|
|
count: 0,
|
|
anchor: "available" %>
|
|
<p class="text-sm text-secondary px-1 py-2"><%= t("settings.providers.groups.empty_available") %></p>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|