mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-09 23:45:21 +02:00
Simplify custom confirmation configuration in views
This commit is contained in:
parent
6659e0cdb5
commit
17d073e58d
17 changed files with 123 additions and 169 deletions
|
@ -5,9 +5,7 @@ class ButtonComponent < ViewComponent::Base
|
|||
|
||||
attr_reader :text, :icon, :icon_position
|
||||
|
||||
def initialize(
|
||||
text: nil, variant: "primary", size: "md", icon: nil, icon_position: "left", full_width: false, rounded: false, **opts
|
||||
)
|
||||
def initialize(text: nil, variant: "primary", size: "md", icon: nil, icon_position: "left", full_width: false, rounded: false, confirm: nil, **opts)
|
||||
@text = text
|
||||
@variant = variant.underscore.to_sym
|
||||
@size = size.to_sym
|
||||
|
@ -15,18 +13,11 @@ class ButtonComponent < ViewComponent::Base
|
|||
@icon_position = icon_position
|
||||
@full_width = full_width
|
||||
@rounded = rounded
|
||||
@confirm = confirm
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def container(&block)
|
||||
merged_opts = opts.dup || {}
|
||||
extra_classes = merged_opts.delete(:class)
|
||||
href = merged_opts.delete(:href)
|
||||
|
||||
merged_opts = merged_opts.merge(
|
||||
class: class_names(container_classes, extra_classes)
|
||||
)
|
||||
|
||||
if href.present?
|
||||
button_to(href, **merged_opts, &block)
|
||||
else
|
||||
|
@ -35,5 +26,25 @@ class ButtonComponent < ViewComponent::Base
|
|||
end
|
||||
|
||||
private
|
||||
attr_reader :variant, :size, :rounded, :full_width, :opts
|
||||
attr_reader :variant, :size, :rounded, :full_width, :confirm, :opts
|
||||
|
||||
def href
|
||||
opts[:href]
|
||||
end
|
||||
|
||||
def merged_opts
|
||||
merged_opts = opts.dup || {}
|
||||
extra_classes = merged_opts.delete(:class)
|
||||
href = merged_opts.delete(:href)
|
||||
data = merged_opts.delete(:data) || {}
|
||||
|
||||
if confirm.present?
|
||||
data = data.merge(turbo_confirm: confirm.to_data_attribute)
|
||||
end
|
||||
|
||||
merged_opts.merge(
|
||||
class: class_names(container_classes, extra_classes),
|
||||
data: data
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
class MenuItemComponent < ViewComponent::Base
|
||||
VARIANTS = %i[link button divider].freeze
|
||||
|
||||
attr_reader :variant, :text, :icon, :href, :method, :destructive, :opts
|
||||
attr_reader :variant, :text, :icon, :href, :method, :destructive, :confirm, :opts
|
||||
|
||||
def initialize(variant:, text: nil, icon: nil, href: nil, method: :post, destructive: false, **opts)
|
||||
def initialize(variant:, text: nil, icon: nil, href: nil, method: :post, destructive: false, confirm: nil, **opts)
|
||||
@variant = variant.to_sym
|
||||
@text = text
|
||||
@icon = icon
|
||||
|
@ -11,13 +11,13 @@ class MenuItemComponent < ViewComponent::Base
|
|||
@method = method.to_sym
|
||||
@destructive = destructive
|
||||
@opts = opts
|
||||
|
||||
@confirm = confirm
|
||||
raise ArgumentError, "Invalid variant: #{@variant}" unless VARIANTS.include?(@variant)
|
||||
end
|
||||
|
||||
def wrapper(&block)
|
||||
if variant == :button
|
||||
button_to href, method: method, class: container_classes, **opts, &block
|
||||
button_to href, method: method, class: container_classes, **merged_button_opts, &block
|
||||
elsif variant == :link
|
||||
link_to href, class: container_classes, **opts, &block
|
||||
else
|
||||
|
@ -43,4 +43,15 @@ class MenuItemComponent < ViewComponent::Base
|
|||
destructive? ? "hover:bg-red-tint-5 theme-dark:hover:bg-red-tint-10" : "hover:bg-container-hover"
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
def merged_button_opts
|
||||
merged_opts = opts.dup || {}
|
||||
data = merged_opts.delete(:data) || {}
|
||||
|
||||
if confirm.present?
|
||||
data = data.merge(turbo_confirm: confirm.to_data_attribute)
|
||||
end
|
||||
|
||||
merged_opts.merge(data: data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
module ApplicationHelper
|
||||
include Pagy::Frontend
|
||||
|
||||
def custom_turbo_confirm(title: "Are you sure?", body: "This action cannot be undone.", btn_text: "Confirm", btn_variant: "primary")
|
||||
{
|
||||
title: title,
|
||||
body: body,
|
||||
confirmText: btn_text,
|
||||
variant: btn_variant
|
||||
}
|
||||
end
|
||||
|
||||
def icon(key, size: "md", color: "current")
|
||||
render partial: "shared/icon", locals: { key:, size:, color: }
|
||||
end
|
||||
|
|
51
app/helpers/custom_confirm.rb
Normal file
51
app/helpers/custom_confirm.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
# The shape of data expected by `confirm_dialog_controller.js` to override the
|
||||
# default browser confirm API via Turbo.
|
||||
class CustomConfirm
|
||||
class << self
|
||||
def for_resource_deletion(resource_name, high_severity: false)
|
||||
new(
|
||||
destructive: true,
|
||||
high_severity: high_severity,
|
||||
title: "Delete #{resource_name}?",
|
||||
body: "Are you sure you want to delete #{resource_name}? This is not reversible.",
|
||||
btn_text: "Delete #{resource_name}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(title: default_title, body: default_body, btn_text: default_btn_text, destructive: false, high_severity: false)
|
||||
@title = title
|
||||
@body = body
|
||||
@btn_text = btn_text
|
||||
@btn_variant = derive_btn_variant(destructive, high_severity)
|
||||
end
|
||||
|
||||
def to_data_attribute
|
||||
{
|
||||
title: title,
|
||||
body: body,
|
||||
confirmText: btn_text,
|
||||
variant: btn_variant
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
attr_reader :title, :body, :btn_text, :btn_variant
|
||||
|
||||
def derive_btn_variant(destructive, high_severity)
|
||||
return "primary" unless destructive
|
||||
high_severity ? "destructive" : "outline-destructive"
|
||||
end
|
||||
|
||||
def default_title
|
||||
"Are you sure?"
|
||||
end
|
||||
|
||||
def default_body
|
||||
"This is not reversible."
|
||||
end
|
||||
|
||||
def default_btn_text
|
||||
"Confirm"
|
||||
end
|
||||
end
|
|
@ -19,14 +19,7 @@
|
|||
href: account_path(account),
|
||||
method: :delete,
|
||||
icon: "trash-2",
|
||||
data: {
|
||||
turbo_frame: :_top,
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_title"),
|
||||
body: t(".confirm_body_html"),
|
||||
btn_text: t(".confirm_accept", name: account.name),
|
||||
btn_variant: "destructive"
|
||||
)
|
||||
}
|
||||
confirm: CustomConfirm.for_resource_deletion("Account", high_severity: true),
|
||||
data: { turbo_frame: :_top }
|
||||
) %>
|
||||
<% end %>
|
||||
|
|
|
@ -4,20 +4,12 @@
|
|||
<div class="flex items-center gap-2">
|
||||
<%= render MenuComponent.new do |menu| %>
|
||||
<% menu.with_item(
|
||||
variant: "button",
|
||||
text: "Delete all",
|
||||
href: destroy_all_categories_path,
|
||||
method: :delete,
|
||||
icon: "trash-2",
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: "Delete all categories?",
|
||||
body: "All of your transactions will become uncategorized and this cannot be undone.",
|
||||
btn_text: "Delete all categories",
|
||||
btn_variant: "destructive"
|
||||
)
|
||||
}
|
||||
) %>
|
||||
variant: "button",
|
||||
text: "Delete all",
|
||||
href: destroy_all_categories_path,
|
||||
method: :delete,
|
||||
icon: "trash-2",
|
||||
confirm: CustomConfirm.for_resource_deletion("All categories", high_severity: true)) %>
|
||||
<% end %>
|
||||
|
||||
<%= render LinkComponent.new(
|
||||
|
|
|
@ -17,13 +17,6 @@
|
|||
href: chat_path(chat),
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: "Delete chat?",
|
||||
body: "Are you sure you want to delete this chat? This action cannot be undone.",
|
||||
btn_text: "Delete chat",
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}) %>
|
||||
confirm: CustomConfirm.for_resource_deletion("Chat")) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -29,13 +29,7 @@
|
|||
href: chat_path(chat),
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: "Are you sure you want to delete this chat?",
|
||||
btn_text: "Delete chat",
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}) %>
|
||||
confirm: CustomConfirm.for_resource_deletion("Chat")) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</nav>
|
||||
|
|
|
@ -23,14 +23,7 @@
|
|||
href: family_merchant_path(family_merchant),
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: "Delete #{family_merchant.name}?",
|
||||
body: "This will remove this merchant from all transactions it has been assigned to.",
|
||||
btn_text: "Delete #{family_merchant.name}",
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}) %>
|
||||
confirm: CustomConfirm.for_resource_deletion(family_merchant.name)) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -46,14 +46,11 @@
|
|||
href: revert_import_path(import),
|
||||
icon: "rotate-ccw",
|
||||
method: :put,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: "Revert import?",
|
||||
body: "This will delete transactions that were imported, but you will still be able to review and re-import your data at any time.",
|
||||
btn_text: "Revert",
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}) %>
|
||||
confirm: CustomConfirm.new(
|
||||
title: "Revert import?",
|
||||
body: "This will delete transactions that were imported, but you will still be able to review and re-import your data at any time.",
|
||||
btn_text: "Revert"
|
||||
)) %>
|
||||
|
||||
<% else %>
|
||||
<% menu.with_item(
|
||||
|
@ -62,14 +59,7 @@
|
|||
href: import_path(import),
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: {
|
||||
title: "Delete import?",
|
||||
body: "This will delete the import and is not reversible.",
|
||||
btn_text: "Delete import",
|
||||
btn_variant: "outline-destructive"
|
||||
}
|
||||
}) %>
|
||||
confirm: CustomConfirm.for_resource_deletion("Import")) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -23,13 +23,7 @@
|
|||
href: merchant_path(merchant),
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: { turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_title"),
|
||||
body: t(".confirm_body"),
|
||||
btn_text: t(".confirm_accept"),
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}) %>
|
||||
confirm: CustomConfirm.for_resource_deletion(merchant.name)) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -80,14 +80,7 @@
|
|||
variant: "destructive",
|
||||
href: plaid_item_path(plaid_item),
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_title"),
|
||||
body: t(".confirm_body"),
|
||||
btn_text: t(".confirm_accept"),
|
||||
btn_variant: "destructive"
|
||||
)
|
||||
}
|
||||
confirm: CustomConfirm.for_resource_deletion(plaid_item.name, high_severity: true)
|
||||
) %>
|
||||
|
||||
<%= render LinkComponent.new(
|
||||
|
@ -115,11 +108,7 @@
|
|||
icon: "trash-2",
|
||||
href: plaid_item_path(plaid_item),
|
||||
method: :delete,
|
||||
data: { turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_title"),
|
||||
body: t(".confirm_body"),
|
||||
btn_text: t(".confirm_accept"),
|
||||
btn_variant: "destructive") }
|
||||
confirm: CustomConfirm.for_resource_deletion(plaid_item.name, high_severity: true)
|
||||
) %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -53,14 +53,7 @@
|
|||
href: rule_path(rule),
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: "Delete rule?",
|
||||
body: "Are you sure you want to delete this rule? Data affected by this rule will no longer be automatically updated. This action cannot be undone.",
|
||||
btn_text: "Delete rule",
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}) %>
|
||||
confirm: CustomConfirm.for_resource_deletion("Rule")) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,14 +10,7 @@
|
|||
href: destroy_all_rules_path,
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: "Delete all rules?",
|
||||
body: "Are you sure you want to delete all rules? This action cannot be undone.",
|
||||
btn_text: "Delete all rules",
|
||||
btn_variant: "destructive"
|
||||
)
|
||||
}) %>
|
||||
confirm: CustomConfirm.for_resource_deletion("All rules", high_severity: true)) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
|
|
@ -56,14 +56,7 @@
|
|||
icon: "x",
|
||||
href: settings_profile_path(user_id: user),
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_remove_member.title"),
|
||||
body: t(".confirm_remove_member.body", name: user.display_name),
|
||||
btn_text: t(".remove_member"),
|
||||
btn_variant: "destructive"
|
||||
)
|
||||
}
|
||||
confirm: CustomConfirm.for_resource_deletion(user.display_name, high_severity: true)
|
||||
) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
@ -110,14 +103,7 @@
|
|||
icon: "x",
|
||||
href: invitation_path(invitation),
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_remove_invitation.title"),
|
||||
body: t(".confirm_remove_invitation.body", email: invitation.email),
|
||||
btn_text: t(".remove_invitation"),
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}
|
||||
confirm: CustomConfirm.for_resource_deletion(invitation.email, high_severity: true)
|
||||
) %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
@ -150,14 +136,7 @@
|
|||
variant: "destructive",
|
||||
href: reset_user_path(@user),
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_reset.title"),
|
||||
body: t(".confirm_reset.body"),
|
||||
btn_text: t(".reset_account"),
|
||||
btn_variant: "destructive"
|
||||
)
|
||||
}
|
||||
confirm: CustomConfirm.for_resource_deletion("Account", high_severity: true)
|
||||
) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
@ -172,14 +151,7 @@
|
|||
variant: "destructive",
|
||||
href: user_path(@user),
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".confirm_delete.title"),
|
||||
body: t(".confirm_delete.body"),
|
||||
btn_text: t(".delete_account"),
|
||||
btn_variant: "destructive"
|
||||
)
|
||||
}
|
||||
confirm: CustomConfirm.for_resource_deletion("your account", high_severity: true)
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -26,14 +26,12 @@
|
|||
variant: "secondary",
|
||||
href: disable_mfa_path,
|
||||
method: :delete,
|
||||
data: {
|
||||
turbo_confirm: custom_turbo_confirm(
|
||||
title: t(".disable_mfa_confirm"),
|
||||
body: t(".disable_mfa_confirm"),
|
||||
btn_text: t(".disable_mfa"),
|
||||
btn_variant: "outline-destructive"
|
||||
)
|
||||
}
|
||||
confirm: CustomConfirm.new(
|
||||
title: t(".disable_mfa_confirm"),
|
||||
body: t(".disable_mfa_confirm"),
|
||||
btn_text: t(".disable_mfa"),
|
||||
destructive: true
|
||||
)
|
||||
) %>
|
||||
<% else %>
|
||||
<%= render LinkComponent.new(
|
||||
|
|
|
@ -17,12 +17,8 @@
|
|||
href: tag_path(tag),
|
||||
icon: "trash-2",
|
||||
method: :delete,
|
||||
data: { turbo_confirm: custom_turbo_confirm(
|
||||
title: "Delete tag?",
|
||||
body: "Are you sure you want to delete this tag and remove it from assigned transactions? This action cannot be undone.",
|
||||
btn_text: "Delete tag",
|
||||
btn_variant: "outline-destructive"
|
||||
) }) %>
|
||||
confirm: CustomConfirm.for_resource_deletion(tag.name)
|
||||
) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue