1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-09 15:35:22 +02:00

Button updates

This commit is contained in:
Zach Gollwitzer 2025-04-25 10:19:58 -04:00
parent fdbfa14779
commit a3fea6164d
40 changed files with 285 additions and 207 deletions

View file

@ -3,7 +3,7 @@
class ButtonComponent < ViewComponent::Base
VARIANTS = {
primary: {
bg: "bg-gray-900 theme-dark:bg-white hover:bg-gray-800 theme-dark:hover:bg-gray-50 disabled:bg-gray-500 theme-dark:disabled:bg-gray-400",
bg: "bg-inverse hover:bg-inverse-hover disabled:bg-gray-500 theme-dark:disabled:bg-gray-400",
text: "text-white theme-dark:text-gray-900",
icon: "fg-inverse"
},
@ -12,6 +12,11 @@ class ButtonComponent < ViewComponent::Base
text: "text-gray-900 theme-dark:text-white",
icon: "fg-primary"
},
destructive: {
bg: "bg-red-500 theme-dark:bg-red-400 hover:bg-red-600 theme-dark:hover:bg-red-500 disabled:bg-red-200 theme-dark:disabled:bg-red-600",
text: "text-white theme-dark:text-white",
icon: "fg-white"
},
outline: {
bg: "bg-transparent hover:bg-gray-100 theme-dark:hover:bg-gray-700",
text: "text-gray-900 theme-dark:text-white",
@ -42,6 +47,11 @@ class ButtonComponent < ViewComponent::Base
bg: "bg-transparent hover:bg-gray-100 theme-dark:hover:bg-gray-700 rounded-lg",
text: "text-secondary",
icon: "fg-gray"
},
icon_inverse: {
bg: "bg-inverse hover:bg-inverse-hover rounded-lg",
text: "fg-inverse",
icon: "fg-inverse"
}
}.freeze
@ -71,7 +81,7 @@ class ButtonComponent < ViewComponent::Base
@variant = (options.delete(:variant) || "primary").underscore.to_sym
@size = (options.delete(:size) || :md).to_sym
@href = options.delete(:href)
@method = options.delete(:method)
@method = options.delete(:method) || :get
@leading_icon = options.delete(:leading_icon)
@trailing_icon = options.delete(:trailing_icon)
@icon = options.delete(:icon)
@ -82,11 +92,12 @@ class ButtonComponent < ViewComponent::Base
end
def wrapper_tag(&block)
if @href && @method
if @href && @method != :get
button_to @href, class: container_classes, method: @method, **@options, &block
elsif @href
link_to @href, class: container_classes, **@options, &block
else
html_tag = @href ? "a" : "button"
content_tag(html_tag, class: container_classes, href: @href, **@options, &block)
content_tag :button, class: container_classes, **@options, &block
end
end
@ -106,7 +117,7 @@ class ButtonComponent < ViewComponent::Base
end
def icon_only?
@variant == :icon
@variant == :icon || @variant == :icon_inverse
end
private
@ -115,7 +126,7 @@ class ButtonComponent < ViewComponent::Base
"inline-flex items-center gap-1",
@full_width ? "w-full" : nil,
@left_align ? "justify-start" : "justify-center",
@variant == :icon ? size_meta[:icon_container] : size_meta[:container],
icon_only? ? size_meta[:icon_container] : size_meta[:container],
variant_meta[:bg],
variant_meta.dig(:border),
@extra_classes

View file

@ -1,31 +1,26 @@
class MenuItemComponent < ViewComponent::Base
erb_template <<~ERB
<%= wrapper do %>
<%= render IconComponent.new(@icon, variant: destructive? ? "destructive" : "default") %>
<% if @icon %>
<%= render IconComponent.new(@icon, variant: destructive? ? "destructive" : "default") %>
<% end %>
<%= tag.span(@text, class: text_classes) %>
<% end %>
ERB
VARIANTS = {
link: {},
action: {}
}
def initialize(text:, href:, variant: "link", method: :post, icon: nil, data: {})
def initialize(text:, href:, method: :get, icon: nil, data: {})
@text = text
@icon = icon
@href = href
@variant = variant.to_sym
@method = method
@method = method.to_sym
@data = data
end
def wrapper(&block)
case @variant
when :link
link_to @href, data: @data, class: container_classes, &block
when :action
if @method.in?([ :post, :patch, :delete ])
button_to @href, method: @method, data: @data, class: container_classes, &block
else
link_to @href, data: @data, class: container_classes, &block
end
end

View file

@ -56,7 +56,7 @@ class StyledFormBuilder < ActionView::Helpers::FormBuilder
opts = options.dup
opts[:data] = { turbo_submits_with: "Submitting..." }.merge(opts[:data] || {})
@template.render(ButtonComponent.new(text: value, **opts))
@template.render(ButtonComponent.new(text: value, full_width: true, **opts))
end
private

View file

@ -5,29 +5,23 @@
<div class="flex items-center justify-between mb-4">
<%= tag.h2 t(".title"), class: "font-medium text-lg" %>
<% unless @account.plaid_account_id.present? %>
<div data-controller="menu" data-testid="activity-menu">
<%= render ButtonComponent.new(text: "New", variant: "secondary", leading_icon: "plus", data: { menu_target: "button" }) %>
<%= render MenuComponent.new(variant: "button") do |menu| %>
<% menu.with_button(text: "New", variant: "secondary", leading_icon: "plus") %>
<div data-menu-target="content" class="z-10 hidden bg-container rounded-lg border border-alpha-black-25 shadow-xs p-1">
<%= render ButtonComponent.new(
<% menu.with_item(
text: "New balance",
variant: "ghost",
full_width: true,
left_align: true,
leading_icon: "circle-dollar-sign",
href: new_valuation_path(account_id: @account.id),
data: { turbo_frame: :modal }) %>
icon: "circle-dollar-sign",
href: new_valuation_path(account_id: @account.id), data: { turbo_frame: :modal }) %>
<% unless @account.crypto? %>
<%= link_to @account.investment? ? new_trade_path(account_id: @account.id) : new_transaction_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "btn btn--primary flex items-center justify-center gap-2 rounded-full md:rounded-lg w-9 h-9 md:w-auto md:h-auto" do %>
<span class="flex items-center justify-center">
<%= lucide_icon("credit-card", class: "text-secondary w-5 h-5") %>
</span>
<%= tag.span t(".new_transaction"), class: "text-sm md:block" %>
<% end %>
<% end %>
</div>
</div>
<% unless @account.crypto? %>
<% href = @account.investment? ? new_trade_path(account_id: @account.id) : new_transaction_path(account_id: @account.id) %>
<% menu.with_item(
text: "New transaction",
icon: "credit-card",
href: href,
data: { turbo_frame: :modal }) %>
<% end %>
<% end %>
<% end %>
</div>

View file

@ -17,7 +17,6 @@
href: account_path(account),
method: :delete,
icon: "trash-2",
variant: "action",
data: { turbo_frame: :_top, turbo_confirm: {
title: t(".confirm_title"),
body: t(".confirm_body_html"),

View file

@ -1,11 +1,9 @@
<div id="<%= dom_id(budget, :confirm_button) %>">
<% if budget.allocations_valid? %>
<%= link_to "Confirm",
budget_path(budget),
class: "block btn btn--primary w-full text-center" %>
<% else %>
<span class="block btn btn--secondary w-full text-center text-subdued cursor-not-allowed">
Confirm
</span>
<% end %>
<%= render ButtonComponent.new(
text: "Confirm",
variant: "primary",
full_width: true,
href: budget_path(budget),
disabled: !budget.allocations_valid?
) %>
</div>

View file

@ -6,12 +6,19 @@
</p>
<div class="flex items-center gap-2">
<%= button_to "Use defaults (recommended)", bootstrap_categories_path, class: "btn btn--primary" %>
<%= render ButtonComponent.new(
text: "Use defaults (recommended)",
href: bootstrap_categories_path,
method: :post
) %>
<%= link_to new_category_path, class: "btn btn--outline flex items-center gap-1", data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-5 h-5") %>
<span>New category</span>
<% end %>
<%= render ButtonComponent.new(
text: "New category",
variant: "outline",
leading_icon: "plus",
href: new_category_path,
data: { turbo_frame: "modal" }
) %>
</div>
</div>
</div>

View file

@ -133,14 +133,17 @@
<% end %>
</ul>
<%= link_to "View all category transactions",
transactions_path(q: {
categories: [@budget_category.name],
start_date: @budget.start_date,
end_date: @budget.end_date
}),
data: { turbo_frame: :_top },
class: "block text-center btn btn--outline w-full" %>
<%= render ButtonComponent.new(
text: "View all category transactions",
variant: "outline",
full_width: true,
href: transactions_path(q: {
categories: [@budget_category.name],
start_date: @budget.start_date,
end_date: @budget.end_date
}),
data: { turbo_frame: :_top }
) %>
<% else %>
<p class="text-secondary text-sm mb-4">
No transactions found for this budget period.

View file

@ -8,24 +8,28 @@
<span>Spent</span>
</div>
<div class="text-3xl font-medium <%= budget.available_to_spend.negative? ? "text-red-500" : "text-primary" %>">
<div class="mb-2 text-3xl font-medium <%= budget.available_to_spend.negative? ? "text-red-500" : "text-primary" %>">
<%= format_money(budget.actual_spending_money) %>
</div>
<%= link_to edit_budget_path(budget), class: "btn btn--secondary flex items-center gap-1 mt-2" do %>
<span class="text-primary font-medium">
of <%= format_money(budget.budgeted_spending_money) %>
</span>
<%= lucide_icon "pencil", class: "w-4 h-4 text-secondary hover:text-gray-600" %>
<% end %>
<%= render ButtonComponent.new(
text: "of #{budget.budgeted_spending_money.format}",
variant: "secondary",
trailing_icon: "pencil",
size: "sm",
href: edit_budget_path(budget)
) %>
<% else %>
<div class="text-subdued text-3xl mb-2">
<span><%= format_money Money.new(0, budget.currency || budget.family.currency) %></span>
</div>
<%= link_to edit_budget_path(budget), class: "flex items-center gap-2 btn btn--primary" do %>
<%= lucide_icon "plus", class: "w-4 h-4 text-white" %>
New budget
<% end %>
<%= render ButtonComponent.new(
text: "New budget",
size: "sm",
leading_icon: "plus",
href: edit_budget_path(budget)
) %>
<% end %>
</div>
@ -41,11 +45,13 @@
<%= format_money(bc.actual_spending_money) %>
</p>
<%= link_to budget_budget_categories_path(budget), class: "btn btn--secondary flex items-center gap-1" do %>
<span>of <%= format_money(bc.budgeted_spending_money, precision: 0) %></span>
<%= lucide_icon "pencil", class: "w-4 h-4 text-secondary shrink-0" %>
<% end %>
<%= render ButtonComponent.new(
text: "of #{bc.budgeted_spending_money.format(precision: 0)}",
variant: "secondary",
trailing_icon: "pencil",
size: "sm",
href: budget_budget_categories_path(budget)
) %>
</div>
</div>
<% end %>

View file

@ -31,10 +31,10 @@
</div>
<div class="ml-auto">
<% if @budget.current? %>
<span class="border border-secondary text-primary text-sm font-medium px-3 py-2 rounded-lg">Today</span>
<% else %>
<%= link_to "Today", budget_path(Budget.date_to_param(Date.current)), class: "btn btn--outline" %>
<% end %>
<%= render ButtonComponent.new(
text: "Today",
variant: "outline",
href: budget_path(Budget.date_to_param(Date.current)),
) %>
</div>
</div>

View file

@ -4,10 +4,11 @@
<%= lucide_icon "alert-triangle", class: "w-6 h-6 text-red-500" %>
<p class="text-secondary text-sm text-center">You have over-allocated your budget. Please fix your allocations.</p>
<%= link_to budget_budget_categories_path(budget), class: "btn btn--secondary flex items-center gap-1" do %>
<span class="text-primary font-medium">
Fix allocations
</span>
<%= lucide_icon "pencil", class: "w-4 h-4 text-secondary hover:text-gray-600" %>
<% end %>
<%= render ButtonComponent.new(
text: "Fix allocations",
variant: "secondary",
size: "sm",
trailing_icon: "pencil",
href: budget_budget_categories_path(budget)
) %>
</div>

View file

@ -38,7 +38,7 @@
<% param_key = Budget.date_to_param(date) %>
<% if Budget.budget_date_valid?(date, family: family) %>
<%= link_to month_name, budget_path(param_key), data: { turbo_frame: "_top" }, class: "btn btn--ghost" %>
<%= render ButtonComponent.new(variant: "ghost", text: month_name, href: budget_path(param_key), data: { turbo_frame: :_top }) %>
<% else %>
<span class="px-3 py-2 text-subdued rounded-md"><%= month_name %></span>
<% end %>

View file

@ -40,7 +40,7 @@
</div>
<% end %>
<%= f.submit "Continue", class: "btn btn--primary w-full" %>
<%= f.submit "Continue" %>
<% end %>
</div>
</div>

View file

@ -54,10 +54,12 @@
<h2 class="text-lg font-medium">Categories</h2>
<% if @budget.initialized? %>
<%= link_to budget_budget_categories_path(@budget), class: "btn btn--secondary flex items-center gap-2" do %>
<%= icon "settings-2", color: "gray" %>
<span>Edit</span>
<% end %>
<%= render ButtonComponent.new(
text: "Edit",
variant: "secondary",
leading_icon: "settings-2",
href: budget_budget_categories_path(@budget)
) %>
<% end %>
</div>

View file

@ -2,18 +2,29 @@
<h1 class="text-primary text-xl font-medium"><%= t(".categories") %></h1>
<div class="flex items-center gap-2">
<%= contextual_menu do %>
<%= contextual_menu_destructive_item "Delete all", destroy_all_categories_path, turbo_confirm: {
title: "Delete all categories?",
body: "All of your transactions will become uncategorized and this cannot be undone.",
accept: "Delete all categories",
} %>
<%= render MenuComponent.new do |menu| %>
<% menu.with_item(
text: "Delete all",
href: destroy_all_categories_path,
method: :delete,
icon: "trash-2",
data: {
turbo_confirm: {
title: "Delete all categories?",
body: "All of your transactions will become uncategorized and this cannot be undone.",
accept: "Delete all categories",
}
}
) %>
<% end %>
<%= link_to new_category_path, class: "btn btn--primary flex items-center gap-1 justify-center", data: { turbo_frame: :modal } do %>
<%= lucide_icon "plus", class: "w-5 h-5" %>
<p><%= t(".new") %></p>
<% end %>
<%= render ButtonComponent.new(
text: t(".new"),
variant: "primary",
leading_icon: "plus",
href: new_category_path,
data: { turbo_frame: :modal }
) %>
</div>
</header>
@ -33,12 +44,19 @@
<div class="text-center flex flex-col items-center max-w-[500px]">
<p class="text-sm text-secondary mb-4"><%= t(".empty") %></p>
<div class="flex items-center gap-2">
<%= button_to t(".bootstrap"), bootstrap_categories_path, class: "btn btn--primary" %>
<%= render ButtonComponent.new(
text: t(".bootstrap"),
href: bootstrap_categories_path,
method: :post
) %>
<%= link_to new_category_path, class: "btn btn--outline flex items-center gap-1", data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-5 h-5") %>
<span><%= t(".new") %></span>
<% end %>
<%= render ButtonComponent.new(
text: t(".new"),
variant: "outline",
leading_icon: "plus",
href: new_category_path,
data: { turbo_frame: :modal }
) %>
</div>
</div>
</div>

View file

@ -22,7 +22,13 @@
<div class="flex justify-center items-center py-12">
<div class="text-center flex flex-col items-center max-w-[500px]">
<p class="text-sm text-secondary font-normal mb-4"><%= t(".empty") %></p>
<%= button_to t(".bootstrap"), bootstrap_categories_path, class: "btn btn--outline", data: { turbo_frame: :_top } %>
<%= render ButtonComponent.new(
text: t(".bootstrap"),
variant: "outline",
href: bootstrap_categories_path,
method: :post,
data: { turbo_frame: :_top }) %>
</div>
</div>
<% end %>

View file

@ -10,8 +10,11 @@
<div class="flex items-center justify-between gap-2">
<p class="text-xs text-red-500">Failed to generate response. Please try again.</p>
<%= button_to retry_chat_path(chat), method: :post, class: "btn btn--primary" do %>
<span>Retry</span>
<% end %>
<%= render ButtonComponent.new(
text: "Retry",
variant: "primary",
href: retry_chat_path(chat),
method: :post
) %>
</div>
</div>

View file

@ -27,5 +27,10 @@
</div>
<div class="flex justify-center py-8">
<%= link_to "Edit account details", edit_credit_card_path(account), class: "btn btn--ghost", data: { turbo_frame: :modal } %>
<%= render ButtonComponent.new(
text: "Edit account details",
variant: "ghost",
href: edit_credit_card_path(account),
data: { turbo_frame: :modal }
) %>
</div>

View file

@ -1,10 +1,11 @@
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium">Merchants</h1>
<%= link_to new_family_merchant_path, class: "btn btn--primary flex items-center gap-2", data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-5 h-5") %>
<p class="text-sm font-medium">New merchant</p>
<% end %>
<%= render ButtonComponent.new(
text: "New merchant",
href: new_family_merchant_path,
data: { turbo_frame: "modal" }
) %>
</header>
<div class="bg-container shadow-border-xs rounded-xl p-4">

View file

@ -17,7 +17,12 @@
<p class="text-green-500 text-sm md:text-base">Your data has been cleaned</p>
</div>
<%= link_to "Next step", import_confirm_path(@import), class: "btn btn--primary w-full md:w-auto" %>
<%= render ButtonComponent.new(
text: "Next step",
href: import_confirm_path(@import),
data: { turbo_frame: :_top },
class: "w-full md:w-auto"
) %>
</div>
<% else %>
<div class="bg-container border border-tertiary rounded-lg p-3 flex flex-col md:flex-row items-start md:items-center justify-between gap-3 md:gap-0">

View file

@ -6,5 +6,5 @@
<%= form.select :amount_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Balance" }, required: true %>
<%= form.select :currency_col_label, import.csv_headers, { include_blank: "Default", label: "Currency" } %>
<%= form.submit "Apply configuration", class: "w-full btn btn--primary", disabled: import.complete? %>
<%= form.submit "Apply configuration", disabled: import.complete? %>
<% end %>

View file

@ -29,5 +29,5 @@
<%= form.select :category_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Category (optional)" }, disabled: import.complete? %>
<%= form.select :tags_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Tags (optional)" }, disabled: import.complete? %>
<%= form.submit "Apply configuration", class: "w-full btn btn--primary", disabled: import.complete? %>
<%= form.submit "Apply configuration", disabled: import.complete? %>
<% end %>

View file

@ -36,5 +36,5 @@
<% end %>
</div>
<%= form.submit "Apply configuration", class: "w-full btn btn--primary", disabled: import.complete? %>
<%= form.submit "Apply configuration", disabled: import.complete? %>
<% end %>

View file

@ -107,7 +107,5 @@
import.csv_headers,
{ include_blank: "Leave empty", label: "Notes" } %>
<%= form.submit "Apply configuration",
class: "w-full btn btn--primary",
disabled: import.complete? %>
<%= form.submit "Apply configuration", disabled: import.complete? %>
<% end %>

View file

@ -18,8 +18,8 @@
<p class="text-sm text-secondary">We found a configuration from a previous import for this account. Would you like to apply it to this import?</p>
<div class="mt-4 flex gap-2 items-center">
<%= link_to "Manually configure", import_configuration_path(@import), class: "btn btn--outline" %>
<%= button_to "Apply template", apply_template_import_path(@import), class: "btn btn--primary", method: :put, data: { turbo_frame: :_top } %>
<%= ButtonComponent.new(text: "Manually configure", href: import_configuration_path(@import), variant: "outline") %>
<%= ButtonComponent.new(text: "Apply template", href: apply_template_import_path(@import), method: :put, data: { turbo_frame: :_top }) %>
</div>
</div>
</div>

View file

@ -8,18 +8,19 @@
<% if import.requires_account? %>
<div class="w-full max-w-full overflow-hidden mb-4">
<div class="overflow-x-auto">
<div class="flex items-center justify-between p-4 gap-4 text-secondary bg-red-100 border border-red-200 rounded-lg w-[650px] min-w-0">
<div class="flex items-center justify-between p-4 gap-4 text-secondary bg-red-100 border border-red-200 rounded-lg w-[650px] min-w-0 mx-auto">
<%= tag.p t(".no_accounts"), class: "text-sm" %>
<%= link_to t(".create_account"), new_account_path(return_to: import_confirm_path(import)), class: "btn btn--primary whitespace-nowrap", data: { turbo_frame: :modal } %>
<%= render ButtonComponent.new(text: "Create account", href: new_account_path(return_to: import_confirm_path(import)), data: { turbo_frame: :modal }) %>
</div>
</div>
</div>
<% elsif import.has_unassigned_account? %>
<div class="w-full max-w-full overflow-hidden mb-4">
<div class="overflow-x-auto">
<div class="flex items-center justify-between p-4 gap-4 text-secondary bg-yellow-100 border border-yellow-200 rounded-lg w-[650px] min-w-0">
<div class="flex items-center justify-between p-4 gap-4 text-secondary bg-yellow-100 border border-yellow-200 rounded-lg w-[650px] min-w-0 mx-auto">
<%= tag.p t(".unassigned_account"), class: "text-sm" %>
<%= link_to t(".create_account"), new_account_path(return_to: import_confirm_path(import)), class: "btn btn--primary whitespace-nowrap", data: { turbo_frame: :modal } %>
<%= render ButtonComponent.new(text: t(".create_account"), href: new_account_path(return_to: import_confirm_path(import)), data: { turbo_frame: :modal }) %>
</div>
</div>
</div>
@ -29,7 +30,7 @@
<div class="space-y-4 w-full max-w-full">
<div class="w-full max-w-full overflow-hidden">
<div class="overflow-x-auto">
<div class="bg-container-inset rounded-xl p-1 space-y-1 w-[650px] min-w-0">
<div class="bg-container-inset rounded-xl p-1 space-y-1 w-[650px] min-w-0 mx-auto">
<div class="grid grid-cols-3 gap-2 text-xs font-medium text-secondary uppercase px-5 py-3">
<p><%= t(".csv_mapping_label", mapping: mapping_label(mapping_class)) %></p>
<p><%= t(".maybe_mapping_label", mapping: mapping_label(mapping_class)) %></p>
@ -48,10 +49,7 @@
</div>
<div class="flex justify-center w-full">
<%= link_to is_last_step ? import_path(import) : url_for(step: step_idx + 2), class: "btn btn--primary w-full md:w-36 flex items-center justify-between gap-2" do %>
<span>Next</span>
<%= lucide_icon "arrow-right", class: "w-5 h-5" %>
<% end %>
<%= render ButtonComponent.new(text: "Next", href: is_last_step ? import_path(import) : url_for(step: step_idx + 2), trailing_icon: "arrow-right", class: "w-full md:w-auto") %>
</div>
</div>
</div>

View file

@ -1,9 +1,7 @@
<div class="flex justify-center items-center py-20">
<div class="text-center flex flex-col items-center max-w-[300px] gap-4">
<p class="text-primary mb-1 font-medium text-sm"><%= t(".message") %></p>
<%= link_to new_import_path, class: "btn btn--primary flex items-center gap-2", data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-5 h-5") %>
<span><%= t(".new") %></span>
<% end %>
<%= render ButtonComponent.new(text: t(".new"), href: new_import_path, leading_icon: "plus", data: { turbo_frame: "modal" }) %>
</div>
</div>

View file

@ -11,8 +11,6 @@
<p class="text-sm text-secondary">Please check that your file format, for any errors and that all required fields are filled, then come back and try again.</p>
</div>
<div>
<%= button_to "Try again", publish_import_path(import), class: "btn btn--primary text-center w-full" %>
</div>
<%= render ButtonComponent.new(text: "Try again", href: publish_import_path(import), method: :post) %>
</div>
</div>

View file

@ -12,8 +12,8 @@
</div>
<div class="space-y-2">
<%= link_to "Check status", import_path(import), class: "block btn btn--primary text-center w-full" %>
<%= link_to "Back to dashboard", root_path, class: "block btn btn--secondary text-center w-full" %>
<%= render ButtonComponent.new(text: "Check status", href: import_path(import)) %>
<%= render ButtonComponent.new(text: "Back to dashboard", href: root_path, variant: "secondary") %>
</div>
</div>
</div>

View file

@ -35,5 +35,5 @@
</div>
</div>
<%= button_to "Publish import", publish_import_path(import), class: "btn btn--primary w-full" %>
<%= ButtonComponent.new(text: "Publish import", href: publish_import_path(import), method: :post) %>
</div>

View file

@ -11,8 +11,11 @@
<p class="text-sm text-secondary">Please try again or contact support.</p>
</div>
<div>
<%= button_to "Try again", revert_import_path(import), class: "btn btn--primary text-center w-full" %>
</div>
<%= render ButtonComponent.new(
text: "Try again",
full_width: true,
href: revert_import_path(import),
method: :post
) %>
</div>
</div>

View file

@ -11,8 +11,10 @@
<p class="text-sm text-secondary">Your imported data has been successfully added to the app and is now ready for use.</p>
</div>
<div>
<%= link_to "Back to dashboard", root_path, class: "block btn btn--primary text-center w-full" %>
</div>
<%= render ButtonComponent.new(
text: "Back to dashboard",
full_width: true,
href: root_path
) %>
</div>
</div>

View file

@ -1,10 +1,12 @@
<div class="flex items-center justify-between">
<h1 class="text-xl font-medium text-primary"><%= t(".title") %></h1>
<%= link_to new_import_path, class: "btn btn--primary flex items-center gap-2", data: { turbo_frame: :modal } do %>
<%= lucide_icon("plus", class: "w-5 h-5") %>
<span><%= t(".new") %></span>
<% end %>
<%= render ButtonComponent.new(
text: "New import",
href: new_import_path,
leading_icon: "plus",
data: { turbo_frame: :modal }
) %>
</div>
<div class="bg-container shadow-border-xs rounded-xl p-4">

View file

@ -45,5 +45,10 @@
</div>
<div class="flex justify-center py-8">
<%= link_to "Edit loan details", edit_loan_path(account), class: "btn btn--ghost", data: { turbo_frame: :modal } %>
<%= render ButtonComponent.new(
text: "Edit loan details",
variant: "ghost",
href: edit_loan_path(account),
data: { turbo_frame: :modal }
) %>
</div>

View file

@ -19,9 +19,11 @@
<% end %>
</div>
<div class="mt-6">
<%= link_to t(".continue"), settings_security_path, class: "w-full btn btn--primary" %>
</div>
<%= render ButtonComponent.new(
text: t(".continue"),
href: settings_security_path,
full_width: true
) %>
</div>
<% end %>

View file

@ -57,7 +57,7 @@
placeholder: t(".code_placeholder") %>
<div class="flex justify-end mt-4">
<%= f.submit t(".verify_button"), class: "btn btn--primary" %>
<%= f.submit t(".verify_button") %>
</div>
</div>
<% end %>

View file

@ -5,7 +5,11 @@
<%= tag.h1 t(".title"), class: "text-3xl font-medium mb-2" %>
<%= tag.p t(".message"), class: "text-sm text-secondary mb-6" %>
<%= link_to t(".setup"), profile_onboarding_path, class: "block flex justify-center items-center btn btn--primary w-full" %>
<%= render ButtonComponent.new(
text: t(".setup"),
href: profile_onboarding_path,
full_width: true
) %>
</div>
</div>
</div>

View file

@ -5,13 +5,13 @@
<p class="text-gray-500">Here's what's happening with your finances</p>
</div>
<%= link_to new_account_path(step: "method_select", classification: "asset"),
class: "btn btn--primary flex items-center justify-center gap-2 rounded-full w-9 h-9 md:hidden",
data: { turbo_frame: "modal" } do %>
<span class="flex items-center justify-center">
<%= lucide_icon("plus", class: "size-5") %>
</span>
<% end %>
<%= render ButtonComponent.new(
variant: "icon-inverse",
icon: "plus",
href: new_account_path(step: "method_select", classification: "asset"),
data: { turbo_frame: "modal" },
class: "rounded-full! md:hidden"
) %>
</div>
<% end %>

View file

@ -7,9 +7,11 @@
<p class="text-secondary"><%= t(".no_account_subtitle") %></p>
</div>
<%= link_to new_account_path, class: "btn btn--primary flex items-center gap-1", data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-5 h-5") %>
<span><%= t(".new_account") %></span>
<% end %>
<%= render ButtonComponent.new(
text: t(".new_account"),
href: new_account_path,
leading_icon: "plus",
data: { turbo_frame: "modal" }
) %>
</div>
</div>

View file

@ -50,49 +50,61 @@
<% if plaid_item.requires_update? %>
<% begin %>
<% link_token = plaid_item.get_update_link_token(webhooks_url: plaid_webhooks_url(plaid_item.plaid_region), redirect_url: accounts_url) %>
<button
data-controller="plaid"
data-action="plaid#open"
data-plaid-region-value="<%= plaid_item.plaid_region %>"
data-plaid-link-token-value="<%= link_token %>"
data-plaid-is-update-value="true"
data-plaid-item-id-value="<%= plaid_item.id %>"
class="btn btn--secondary flex items-center gap-2">
<%= lucide_icon "refresh-cw", class: "w-4 h-4" %>
<%= tag.span t(".update") %>
</button>
<%= render ButtonComponent.new(
text: t(".update"),
leading_icon: "refresh-cw",
variant: "secondary",
data: {
controller: "plaid",
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-amber-500 flex items-center gap-1">
<%= lucide_icon "alert-triangle", class: "w-4 h-4" %>
<%= tag.span t(".connection_lost") %>
</div>
<p class="text-sm text-secondary"><%= t(".connection_lost_description") %></p>
<div class="flex items-center gap-2">
<%= button_to plaid_item_path(plaid_item),
method: :delete,
class: "btn btn--danger flex items-center gap-2",
data: {
turbo_confirm: {
title: t(".confirm_title"),
body: t(".confirm_body"),
accept: t(".confirm_accept")
}
} do %>
<%= lucide_icon "trash-2", class: "w-4 h-4" %>
<%= tag.span t(".delete") %>
<% end %>
<%= link_to new_account_path, class: "btn btn--secondary flex items-center gap-2" do %>
<%= lucide_icon "plus", class: "w-4 h-4" %>
<%= tag.span t(".add_new") %>
<% end %>
<%= render ButtonComponent.new(
text: t(".delete"),
leading_icon: "trash-2",
variant: "destructive",
method: :delete,
data: {
turbo_confirm: {
title: t(".confirm_title"),
body: t(".confirm_body"),
accept: t(".confirm_accept")
}
}
) %>
<%= render ButtonComponent.new(
text: t(".add_new"),
leading_icon: "plus",
variant: "secondary",
href: new_account_path
) %>
</div>
</div>
<% end %>
<% else %>
<%= button_to sync_plaid_item_path(plaid_item), disabled: plaid_item.syncing? || plaid_item.scheduled_for_deletion?, class: "disabled:text-subdued text-primary flex hover:text-gray-800 items-center text-sm font-medium hover:underline" do %>
<%= lucide_icon "refresh-cw", class: "w-4 h-4" %>
<% end %>
<%= render ButtonComponent.new(
variant: "icon",
icon: "refresh-cw",
href: sync_plaid_item_path(plaid_item),
method: :post,
disabled: plaid_item.syncing? || plaid_item.scheduled_for_deletion?
) %>
<% end %>
<%= contextual_menu do %>