mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-19 21:29:38 +02:00
Lazy load Plaid link tokens, fix link issues on broadcast (#2302)
* Lazy load Plaid link tokens, fix link issues on broadcast * Fix alert styles
This commit is contained in:
parent
c701755b02
commit
d21e385962
19 changed files with 155 additions and 123 deletions
|
@ -5,7 +5,7 @@ module AccountableResource
|
||||||
include ScrollFocusable, Periodable
|
include ScrollFocusable, Periodable
|
||||||
|
|
||||||
before_action :set_account, only: [ :show, :edit, :update, :destroy ]
|
before_action :set_account, only: [ :show, :edit, :update, :destroy ]
|
||||||
before_action :set_link_token, only: :new
|
before_action :set_link_options, only: :new
|
||||||
end
|
end
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
|
@ -59,34 +59,9 @@ module AccountableResource
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def set_link_token
|
def set_link_options
|
||||||
@us_link_token = Current.family.get_link_token(
|
@show_us_link = Current.family.can_connect_plaid_us?
|
||||||
webhooks_url: plaid_us_webhooks_url,
|
@show_eu_link = Current.family.can_connect_plaid_eu?
|
||||||
redirect_url: accounts_url,
|
|
||||||
accountable_type: accountable_type.name,
|
|
||||||
region: :us
|
|
||||||
)
|
|
||||||
|
|
||||||
if Current.family.eu?
|
|
||||||
@eu_link_token = Current.family.get_link_token(
|
|
||||||
webhooks_url: plaid_eu_webhooks_url,
|
|
||||||
redirect_url: accounts_url,
|
|
||||||
accountable_type: accountable_type.name,
|
|
||||||
region: :eu
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def plaid_us_webhooks_url
|
|
||||||
return webhooks_plaid_url if Rails.env.production?
|
|
||||||
|
|
||||||
ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/")) + "/webhooks/plaid"
|
|
||||||
end
|
|
||||||
|
|
||||||
def plaid_eu_webhooks_url
|
|
||||||
return webhooks_plaid_eu_url if Rails.env.production?
|
|
||||||
|
|
||||||
ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/")) + "/webhooks/plaid_eu"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def accountable_type
|
def accountable_type
|
||||||
|
|
|
@ -1,5 +1,26 @@
|
||||||
class PlaidItemsController < ApplicationController
|
class PlaidItemsController < ApplicationController
|
||||||
before_action :set_plaid_item, only: %i[destroy sync]
|
before_action :set_plaid_item, only: %i[edit destroy sync]
|
||||||
|
|
||||||
|
def new
|
||||||
|
region = params[:region] == "eu" ? :eu : :us
|
||||||
|
webhooks_url = region == :eu ? plaid_eu_webhooks_url : plaid_us_webhooks_url
|
||||||
|
|
||||||
|
@link_token = Current.family.get_link_token(
|
||||||
|
webhooks_url: webhooks_url,
|
||||||
|
redirect_url: accounts_url,
|
||||||
|
accountable_type: params[:accountable_type] || "Depository",
|
||||||
|
region: region
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
webhooks_url = @plaid_item.plaid_region == "eu" ? plaid_eu_webhooks_url : plaid_us_webhooks_url
|
||||||
|
|
||||||
|
@link_token = @plaid_item.get_update_link_token(
|
||||||
|
webhooks_url: webhooks_url,
|
||||||
|
redirect_url: accounts_url,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
Current.family.create_plaid_item!(
|
Current.family.create_plaid_item!(
|
||||||
|
@ -39,4 +60,16 @@ class PlaidItemsController < ApplicationController
|
||||||
def item_name
|
def item_name
|
||||||
plaid_item_params.dig(:metadata, :institution, :name)
|
plaid_item_params.dig(:metadata, :institution, :name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def plaid_us_webhooks_url
|
||||||
|
return webhooks_plaid_url if Rails.env.production?
|
||||||
|
|
||||||
|
ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/")) + "/webhooks/plaid"
|
||||||
|
end
|
||||||
|
|
||||||
|
def plaid_eu_webhooks_url
|
||||||
|
return webhooks_plaid_eu_url if Rails.env.production?
|
||||||
|
|
||||||
|
ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/")) + "/webhooks/plaid_eu"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
module PlaidHelper
|
|
||||||
def plaid_webhooks_url(region = :us)
|
|
||||||
if Rails.env.production?
|
|
||||||
region.to_sym == :eu ? webhooks_plaid_eu_url : webhooks_plaid_url
|
|
||||||
else
|
|
||||||
ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/")) + "/webhooks/plaid#{region.to_sym == :eu ? '_eu' : ''}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -6,16 +6,20 @@ export default class extends Controller {
|
||||||
linkToken: String,
|
linkToken: String,
|
||||||
region: { type: String, default: "us" },
|
region: { type: String, default: "us" },
|
||||||
isUpdate: { type: Boolean, default: false },
|
isUpdate: { type: Boolean, default: false },
|
||||||
itemId: String
|
itemId: String,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.open();
|
||||||
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
const handler = Plaid.create({
|
const handler = Plaid.create({
|
||||||
token: this.linkTokenValue,
|
token: this.linkTokenValue,
|
||||||
onSuccess: this.handleSuccess,
|
onSuccess: this.handleSuccess,
|
||||||
onLoad: this.handleLoad,
|
onLoad: this.handleLoad,
|
||||||
onExit: this.handleExit,
|
onExit: this.handleExit,
|
||||||
onEvent: this.handleEvent
|
onEvent: this.handleEvent,
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.open();
|
handler.open();
|
||||||
|
@ -27,10 +31,10 @@ export default class extends Controller {
|
||||||
fetch(`/plaid_items/${this.itemIdValue}/sync`, {
|
fetch(`/plaid_items/${this.itemIdValue}/sync`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
Accept: "application/json",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-CSRF-Token": document.querySelector('[name="csrf-token"]').content,
|
"X-CSRF-Token": document.querySelector('[name="csrf-token"]').content,
|
||||||
}
|
},
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Refresh the page to show the updated status
|
// Refresh the page to show the updated status
|
||||||
window.location.href = "/accounts";
|
window.location.href = "/accounts";
|
||||||
|
|
|
@ -5,6 +5,15 @@ module Family::PlaidConnectable
|
||||||
has_many :plaid_items, dependent: :destroy
|
has_many :plaid_items, dependent: :destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_connect_plaid_us?
|
||||||
|
plaid(:us).present?
|
||||||
|
end
|
||||||
|
|
||||||
|
# If Plaid provider is configured and user is in the EU region
|
||||||
|
def can_connect_plaid_eu?
|
||||||
|
plaid(:eu).present? && self.eu?
|
||||||
|
end
|
||||||
|
|
||||||
def create_plaid_item!(public_token:, item_name:, region:)
|
def create_plaid_item!(public_token:, item_name:, region:)
|
||||||
public_token_response = plaid(region).exchange_public_token(public_token)
|
public_token_response = plaid(region).exchange_public_token(public_token)
|
||||||
|
|
||||||
|
@ -34,6 +43,6 @@ module Family::PlaidConnectable
|
||||||
|
|
||||||
private
|
private
|
||||||
def plaid(region)
|
def plaid(region)
|
||||||
@plaid ||= Provider::Registry.plaid_provider_for_region(region)
|
Provider::Registry.plaid_provider_for_region(region)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,7 +23,6 @@ class PlaidItem < ApplicationRecord
|
||||||
scope :needs_update, -> { where(status: :requires_update) }
|
scope :needs_update, -> { where(status: :requires_update) }
|
||||||
|
|
||||||
def get_update_link_token(webhooks_url:, redirect_url:)
|
def get_update_link_token(webhooks_url:, redirect_url:)
|
||||||
begin
|
|
||||||
family.get_link_token(
|
family.get_link_token(
|
||||||
webhooks_url: webhooks_url,
|
webhooks_url: webhooks_url,
|
||||||
redirect_url: redirect_url,
|
redirect_url: redirect_url,
|
||||||
|
@ -36,11 +35,10 @@ class PlaidItem < ApplicationRecord
|
||||||
if error_body["error_code"] == "ITEM_NOT_FOUND"
|
if error_body["error_code"] == "ITEM_NOT_FOUND"
|
||||||
# Mark the connection as invalid but don't auto-delete
|
# Mark the connection as invalid but don't auto-delete
|
||||||
update!(status: :requires_update)
|
update!(status: :requires_update)
|
||||||
raise PlaidConnectionLostError
|
|
||||||
else
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Sentry.capture_exception(e)
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_later
|
def destroy_later
|
||||||
|
@ -118,6 +116,4 @@ class PlaidItem < ApplicationRecord
|
||||||
def supported_products
|
def supported_products
|
||||||
available_products + billed_products
|
available_products + billed_products
|
||||||
end
|
end
|
||||||
|
|
||||||
class PlaidConnectionLostError < StandardError; end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class PlaidItem::WebhookProcessor
|
||||||
@webhook_type = parsed["webhook_type"]
|
@webhook_type = parsed["webhook_type"]
|
||||||
@webhook_code = parsed["webhook_code"]
|
@webhook_code = parsed["webhook_code"]
|
||||||
@item_id = parsed["item_id"]
|
@item_id = parsed["item_id"]
|
||||||
|
@error = parsed["error"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def process
|
def process
|
||||||
|
@ -21,6 +22,10 @@ class PlaidItem::WebhookProcessor
|
||||||
plaid_item.sync_later
|
plaid_item.sync_later
|
||||||
when [ "HOLDINGS", "DEFAULT_UPDATE" ]
|
when [ "HOLDINGS", "DEFAULT_UPDATE" ]
|
||||||
plaid_item.sync_later
|
plaid_item.sync_later
|
||||||
|
when [ "ITEM", "ERROR" ]
|
||||||
|
if error["error_code"] == "ITEM_LOGIN_REQUIRED"
|
||||||
|
plaid_item.update!(status: :requires_update)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
Rails.logger.warn("Unhandled Plaid webhook type: #{webhook_type}:#{webhook_code}")
|
Rails.logger.warn("Unhandled Plaid webhook type: #{webhook_type}:#{webhook_code}")
|
||||||
end
|
end
|
||||||
|
@ -30,7 +35,7 @@ class PlaidItem::WebhookProcessor
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
attr_reader :webhook_type, :webhook_code, :item_id
|
attr_reader :webhook_type, :webhook_code, :item_id, :error
|
||||||
|
|
||||||
def plaid_item
|
def plaid_item
|
||||||
@plaid_item ||= PlaidItem.find_by(plaid_id: item_id)
|
@plaid_item ||= PlaidItem.find_by(plaid_id: item_id)
|
||||||
|
|
|
@ -28,6 +28,14 @@ class Provider::PlaidSandbox < Provider::Plaid
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reset_login(item)
|
||||||
|
client.sandbox_item_reset_login(
|
||||||
|
Plaid::SandboxItemResetLoginRequest.new(
|
||||||
|
access_token: item.access_token
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def create_client
|
def create_client
|
||||||
raise "Plaid sandbox is not supported in production" if Rails.env.production?
|
raise "Plaid sandbox is not supported in production" if Rails.env.production?
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<%# locals: (path:, us_link_token: nil, eu_link_token: nil) %>
|
<%# locals: (path:, accountable_type:, show_us_link: true, show_eu_link: true) %>
|
||||||
|
|
||||||
<%= render layout: "accounts/new/container", locals: { title: t(".title"), back_path: new_account_path } do %>
|
<%= render layout: "accounts/new/container", locals: { title: t(".title"), back_path: new_account_path } do %>
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
|
@ -9,24 +9,28 @@
|
||||||
<%= t("accounts.new.method_selector.manual_entry") %>
|
<%= t("accounts.new.method_selector.manual_entry") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if us_link_token %>
|
<% if show_us_link %>
|
||||||
<%# Default US-only Link %>
|
<%# Default US-only Link %>
|
||||||
<button data-controller="plaid" data-action="plaid#open dialog#close" data-plaid-region-value="us" data-plaid-link-token-value="<%= us_link_token %>" class="text-primary flex items-center gap-4 w-full text-center focus:outline-hidden focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2">
|
<%= link_to new_plaid_item_path(region: "us", accountable_type: accountable_type),
|
||||||
|
class: "text-primary flex items-center gap-4 w-full text-center focus:outline-hidden focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2",
|
||||||
|
data: { turbo_frame: "modal" } do %>
|
||||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||||
<%= icon("link-2") %>
|
<%= icon("link-2") %>
|
||||||
</span>
|
</span>
|
||||||
<%= t("accounts.new.method_selector.connected_entry") %>
|
<%= t("accounts.new.method_selector.connected_entry") %>
|
||||||
</button>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%# EU Link %>
|
<%# EU Link %>
|
||||||
<% if eu_link_token %>
|
<% if show_eu_link %>
|
||||||
<button data-controller="plaid" data-action="plaid#open dialog#close" data-plaid-region-value="eu" data-plaid-link-token-value="<%= eu_link_token %>" class="text-primary flex items-center gap-4 w-full text-center focus:outline-hidden focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2">
|
<%= link_to new_plaid_item_path(region: "eu", accountable_type: accountable_type),
|
||||||
|
class: "text-primary flex items-center gap-4 w-full text-center focus:outline-hidden focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2",
|
||||||
|
data: { turbo_frame: "modal" } do %>
|
||||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||||
<%= icon("link-2") %>
|
<%= icon("link-2") %>
|
||||||
</span>
|
</span>
|
||||||
<%= t("accounts.new.method_selector.connected_entry_eu") %>
|
<%= t("accounts.new.method_selector.connected_entry_eu") %>
|
||||||
</button>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_credit_card_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
<%= render "accounts/new/method_selector",
|
||||||
|
path: new_credit_card_path(return_to: params[:return_to]),
|
||||||
|
show_us_link: @show_us_link,
|
||||||
|
show_eu_link: @show_eu_link,
|
||||||
|
accountable_type: "CreditCard" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render DialogComponent.new do |dialog| %>
|
<%= render DialogComponent.new do |dialog| %>
|
||||||
<% dialog.with_header(title: t(".title")) %>
|
<% dialog.with_header(title: t(".title")) %>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_crypto_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
<%= render "accounts/new/method_selector",
|
||||||
|
path: new_crypto_path(return_to: params[:return_to]),
|
||||||
|
show_us_link: @show_us_link,
|
||||||
|
show_eu_link: @show_eu_link,
|
||||||
|
accountable_type: "Crypto" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render DialogComponent.new do |dialog| %>
|
<%= render DialogComponent.new do |dialog| %>
|
||||||
<% dialog.with_header(title: t(".title")) %>
|
<% dialog.with_header(title: t(".title")) %>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_depository_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
<%= render "accounts/new/method_selector",
|
||||||
|
path: new_depository_path(return_to: params[:return_to]),
|
||||||
|
show_us_link: @show_us_link,
|
||||||
|
show_eu_link: @show_eu_link,
|
||||||
|
accountable_type: "Depository" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render DialogComponent.new do |dialog| %>
|
<%= render DialogComponent.new do |dialog| %>
|
||||||
<% dialog.with_header(title: t(".title")) %>
|
<% dialog.with_header(title: t(".title")) %>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_investment_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
<%= render "accounts/new/method_selector",
|
||||||
|
path: new_investment_path(return_to: params[:return_to]),
|
||||||
|
show_us_link: @show_us_link,
|
||||||
|
show_eu_link: @show_eu_link,
|
||||||
|
accountable_type: "Investment" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render DialogComponent.new do |dialog| %>
|
<%= render DialogComponent.new do |dialog| %>
|
||||||
<% dialog.with_header(title: t(".title")) %>
|
<% dialog.with_header(title: t(".title")) %>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_loan_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
<%= render "accounts/new/method_selector",
|
||||||
|
path: new_loan_path(return_to: params[:return_to]),
|
||||||
|
show_us_link: @show_us_link,
|
||||||
|
show_eu_link: @show_eu_link,
|
||||||
|
accountable_type: "Loan" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render DialogComponent.new do |dialog| %>
|
<%= render DialogComponent.new do |dialog| %>
|
||||||
<% dialog.with_header(title: t(".title")) %>
|
<% dialog.with_header(title: t(".title")) %>
|
||||||
|
|
9
app/views/plaid_items/_auto_link_opener.html.erb
Normal file
9
app/views/plaid_items/_auto_link_opener.html.erb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<%# locals: (link_token:, region:, item_id:, is_update: false) %>
|
||||||
|
|
||||||
|
<%= tag.div data: {
|
||||||
|
controller: "plaid",
|
||||||
|
plaid_link_token_value: link_token,
|
||||||
|
plaid_region_value: region,
|
||||||
|
plaid_item_id_value: item_id,
|
||||||
|
plaid_is_update_value: is_update
|
||||||
|
} %>
|
|
@ -30,7 +30,7 @@
|
||||||
</div>
|
</div>
|
||||||
<% elsif plaid_item.requires_update? %>
|
<% elsif plaid_item.requires_update? %>
|
||||||
<div class="text-warning flex items-center gap-1">
|
<div class="text-warning flex items-center gap-1">
|
||||||
<%= icon "alert-triangle", size: "sm" %>
|
<%= icon "alert-triangle", size: "sm", color: "warning" %>
|
||||||
<%= tag.span t(".requires_update") %>
|
<%= tag.span t(".requires_update") %>
|
||||||
</div>
|
</div>
|
||||||
<% elsif plaid_item.sync_error.present? %>
|
<% elsif plaid_item.sync_error.present? %>
|
||||||
|
@ -48,56 +48,18 @@
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<% if plaid_item.requires_update? %>
|
<% if plaid_item.requires_update? %>
|
||||||
<% begin %>
|
<%= render LinkComponent.new(
|
||||||
<% link_token = plaid_item.get_update_link_token(webhooks_url: plaid_webhooks_url(plaid_item.plaid_region), redirect_url: accounts_url) %>
|
|
||||||
|
|
||||||
<%= render ButtonComponent.new(
|
|
||||||
text: t(".update"),
|
text: t(".update"),
|
||||||
icon: "refresh-cw",
|
icon: "refresh-cw",
|
||||||
variant: "secondary",
|
variant: "secondary",
|
||||||
data: {
|
href: edit_plaid_item_path(plaid_item),
|
||||||
controller: "plaid",
|
frame: "modal"
|
||||||
action: "plaid#open",
|
|
||||||
plaid_region_value: plaid_item.plaid_region,
|
|
||||||
plaid_link_token_value: link_token,
|
|
||||||
plaid_is_update_value: true,
|
|
||||||
plaid_item_id_value: plaid_item.id
|
|
||||||
}
|
|
||||||
) %>
|
) %>
|
||||||
<% rescue PlaidItem::PlaidConnectionLostError %>
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<div class="text-warning flex items-center gap-1">
|
|
||||||
<%= icon "alert-triangle", size: "sm", color: "warning" %>
|
|
||||||
<%= tag.span t(".connection_lost") %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="text-sm text-secondary"><%= t(".connection_lost_description") %></p>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<%= render ButtonComponent.new(
|
|
||||||
text: t(".delete"),
|
|
||||||
icon: "trash-2",
|
|
||||||
variant: "destructive",
|
|
||||||
href: plaid_item_path(plaid_item),
|
|
||||||
method: :delete,
|
|
||||||
confirm: CustomConfirm.for_resource_deletion(plaid_item.name, high_severity: true)
|
|
||||||
) %>
|
|
||||||
|
|
||||||
<%= render LinkComponent.new(
|
|
||||||
text: t(".add_new"),
|
|
||||||
icon: "plus",
|
|
||||||
variant: "secondary",
|
|
||||||
href: new_account_path
|
|
||||||
) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<% elsif Rails.env.development? %>
|
<% elsif Rails.env.development? %>
|
||||||
<%= icon(
|
<%= icon(
|
||||||
"refresh-cw",
|
"refresh-cw",
|
||||||
as_button: true,
|
as_button: true,
|
||||||
href: sync_plaid_item_path(plaid_item),
|
href: sync_plaid_item_path(plaid_item)
|
||||||
disabled: plaid_item.syncing? || plaid_item.scheduled_for_deletion?
|
|
||||||
) %>
|
) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
8
app/views/plaid_items/edit.html.erb
Normal file
8
app/views/plaid_items/edit.html.erb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<%# We render this in the empty modal frame so if Plaid flow is closed, user stays on same page they were on %>
|
||||||
|
<%= turbo_frame_tag "modal" do %>
|
||||||
|
<%= render "plaid_items/auto_link_opener",
|
||||||
|
link_token: @link_token,
|
||||||
|
region: @plaid_item.plaid_region,
|
||||||
|
item_id: @plaid_item.id,
|
||||||
|
is_update: true %>
|
||||||
|
<% end %>
|
8
app/views/plaid_items/new.html.erb
Normal file
8
app/views/plaid_items/new.html.erb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<%# We render this in the empty modal frame so if Plaid flow is closed, user stays on same page they were on %>
|
||||||
|
<%= turbo_frame_tag "modal" do %>
|
||||||
|
<%= render "plaid_items/auto_link_opener",
|
||||||
|
link_token: @link_token,
|
||||||
|
region: params[:region],
|
||||||
|
item_id: "",
|
||||||
|
is_update: false %>
|
||||||
|
<% end %>
|
|
@ -193,7 +193,7 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :plaid_items, only: %i[create destroy] do
|
resources :plaid_items, only: %i[new edit create destroy] do
|
||||||
member do
|
member do
|
||||||
post :sync
|
post :sync
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue