1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-19 13:19:39 +02:00

Component namespacing (#2463)
Some checks are pending
Publish Docker image / ci (push) Waiting to run
Publish Docker image / Build docker image (push) Blocked by required conditions

* [claudesquad] update from 'component-namespacing' on 18 Jul 25 07:23 EDT

* [claudesquad] update from 'component-namespacing' on 18 Jul 25 07:30 EDT

* Update stimulus controller references to use namespace

* Fix remaining tests
This commit is contained in:
Zach Gollwitzer 2025-07-18 08:30:00 -04:00 committed by GitHub
parent d5b147f2cd
commit ab6fdbbb68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
182 changed files with 322 additions and 321 deletions

View file

@ -1,4 +1,4 @@
class AlertComponent < ViewComponent::Base
class DS::Alert < DesignSystemComponent
def initialize(message:, variant: :info)
@message = message
@variant = variant

View file

@ -2,7 +2,7 @@
# An extension to `button_to` helper. All options are passed through to the `button_to` helper with some additional
# options available.
class ButtonComponent < ButtonishComponent
class DS::Button < DS::Buttonish
attr_reader :confirm
def initialize(confirm: nil, **opts)

View file

@ -1,4 +1,4 @@
class ButtonishComponent < ViewComponent::Base
class DS::Buttonish < DesignSystemComponent
VARIANTS = {
primary: {
container_classes: "text-inverse bg-inverse hover:bg-inverse-hover disabled:bg-gray-500 theme-dark:disabled:bg-gray-400",
@ -71,7 +71,7 @@ class ButtonishComponent < ViewComponent::Base
end
def call
raise NotImplementedError, "ButtonishComponent is an abstract class and cannot be instantiated directly."
raise NotImplementedError, "Buttonish is an abstract class and cannot be instantiated directly."
end
def container_classes(override_classes = nil)

View file

@ -1,7 +1,7 @@
<%= wrapper_element do %>
<%= tag.dialog class: "w-full h-full bg-transparent theme-dark:backdrop:bg-alpha-black-900 backdrop:bg-overlay #{drawer? ? "lg:p-3" : "lg:p-1"}", **merged_opts do %>
<%= tag.div class: dialog_outer_classes do %>
<%= tag.div class: dialog_inner_classes, data: { dialog_target: "content" } do %>
<%= tag.div class: dialog_inner_classes, data: { DS__dialog_target: "content" } do %>
<div class="grow overflow-y-auto py-4 space-y-4 flex flex-col">
<% if header? %>
<%= header %>

View file

@ -1,9 +1,9 @@
class DialogComponent < ViewComponent::Base
class DS::Dialog < DesignSystemComponent
renders_one :header, ->(title: nil, subtitle: nil, hide_close_icon: false, **opts, &block) do
content_tag(:header, class: "px-4 flex flex-col gap-2", **opts) do
title_div = content_tag(:div, class: "flex items-center justify-between gap-2") do
title = content_tag(:h2, title, class: class_names("font-medium text-primary", drawer? ? "text-lg" : "")) if title
close_icon = render ButtonComponent.new(variant: "icon", class: "ml-auto", icon: "x", tabindex: "-1", data: { action: "dialog#close" }) unless hide_close_icon
close_icon = render DS::Button.new(variant: "icon", class: "ml-auto", icon: "x", tabindex: "-1", data: { action: "DS--dialog#close" }) unless hide_close_icon
safe_join([ title, close_icon ].compact)
end
@ -19,16 +19,16 @@ class DialogComponent < ViewComponent::Base
renders_many :actions, ->(cancel_action: false, **button_opts) do
merged_opts = if cancel_action
button_opts.merge(type: "button", data: { action: "modal#close" })
button_opts.merge(type: "button", data: { action: "DS--dialog#close" })
else
button_opts
end
render ButtonComponent.new(**merged_opts)
render DS::Button.new(**merged_opts)
end
renders_many :sections, ->(title:, **disclosure_opts, &block) do
render DisclosureComponent.new(title: title, align: :right, **disclosure_opts) do
render DS::Disclosure.new(title: title, align: :right, **disclosure_opts) do
block.call
end
end
@ -99,11 +99,11 @@ class DialogComponent < ViewComponent::Base
merged_opts = opts.dup
data = merged_opts.delete(:data) || {}
data[:controller] = [ "dialog", "hotkey", data[:controller] ].compact.join(" ")
data[:dialog_auto_open_value] = auto_open
data[:dialog_reload_on_close_value] = reload_on_close
data[:action] = [ "mousedown->dialog#clickOutside", data[:action] ].compact.join(" ")
data[:hotkey] = "esc:dialog#close"
data[:controller] = [ "DS--dialog", "hotkey", data[:controller] ].compact.join(" ")
data[:DS__dialog_auto_open_value] = auto_open
data[:DS__dialog_reload_on_close_value] = reload_on_close
data[:action] = [ "mousedown->DS--dialog#clickOutside", data[:action] ].compact.join(" ")
data[:hotkey] = "esc:DS--dialog#close"
merged_opts[:data] = data
merged_opts

View file

@ -1,4 +1,4 @@
class DisclosureComponent < ViewComponent::Base
class DS::Disclosure < DesignSystemComponent
renders_one :summary_content
attr_reader :title, :align, :open, :opts

View file

@ -1,4 +1,4 @@
class FilledIconComponent < ViewComponent::Base
class DS::FilledIcon < DesignSystemComponent
attr_reader :icon, :text, :hex_color, :size, :rounded, :variant
VARIANTS = %i[default text surface container inverse].freeze

View file

@ -1,6 +1,6 @@
# An extension to `link_to` helper. All options are passed through to the `link_to` helper with some additional
# options available.
class LinkComponent < ButtonishComponent
class DS::Link < DS::Buttonish
attr_reader :frame
VARIANTS = VARIANTS.reverse_merge(

View file

@ -1,17 +1,17 @@
<%= tag.div data: { controller: "menu", menu_placement_value: placement, menu_offset_value: offset, testid: testid } do %>
<%= tag.div data: { controller: "DS--menu", DS__menu_placement_value: placement, DS__menu_offset_value: offset, testid: testid } do %>
<% if variant == :icon %>
<%= render ButtonComponent.new(variant: "icon", icon: icon_vertical ? "more-vertical" : "more-horizontal", data: { menu_target: "button" }) %>
<%= render DS::Button.new(variant: "icon", icon: icon_vertical ? "more-vertical" : "more-horizontal", data: { DS__menu_target: "button" }) %>
<% elsif variant == :button %>
<%= button %>
<% elsif variant == :avatar %>
<button data-menu-target="button">
<button data-DS--menu-target="button">
<div class="w-9 h-9 cursor-pointer">
<%= render "settings/user_avatar", avatar_url: avatar_url, initials: initials %>
</div>
</button>
<% end %>
<div data-menu-target="content" class="px-2 lg:px-0 max-w-full hidden z-50">
<div data-DS--menu-target="content" class="px-2 lg:px-0 max-w-full hidden z-50">
<div class="mx-auto min-w-[200px] shadow-border-xs bg-container rounded-lg">
<%= header %>

View file

@ -1,15 +1,15 @@
# frozen_string_literal: true
class MenuComponent < ViewComponent::Base
class DS::Menu < DesignSystemComponent
attr_reader :variant, :avatar_url, :initials, :placement, :offset, :icon_vertical, :no_padding, :testid
renders_one :button, ->(**button_options, &block) do
options_with_target = button_options.merge(data: { menu_target: "button" })
options_with_target = button_options.merge(data: { DS__menu_target: "button" })
if block
content_tag(:button, **options_with_target, &block)
else
ButtonComponent.new(**options_with_target)
DS::Button.new(**options_with_target)
end
end
@ -19,7 +19,7 @@ class MenuComponent < ViewComponent::Base
renders_one :custom_content
renders_many :items, MenuItemComponent
renders_many :items, DS::MenuItem
VARIANTS = %i[icon button avatar].freeze

View file

@ -1,4 +1,4 @@
class MenuItemComponent < ViewComponent::Base
class DS::MenuItem < DesignSystemComponent
VARIANTS = %i[link button divider].freeze
attr_reader :variant, :text, :icon, :href, :method, :destructive, :confirm, :frame, :opts

View file

@ -1,4 +1,4 @@
class TabComponent < ViewComponent::Base
class DS::Tab < DesignSystemComponent
attr_reader :id, :label
def initialize(id:, label:)

View file

@ -0,0 +1,18 @@
<%= tag.div data: {
controller: "DS--tabs",
testid: testid,
DS__tabs_session_key_value: session_key,
DS__tabs_url_param_key_value: url_param_key,
DS__tabs_nav_btn_active_class: active_btn_classes,
DS__tabs_nav_btn_inactive_class: inactive_btn_classes
} do %>
<% if unstyled? %>
<%= content %>
<% else %>
<%= nav %>
<% panels.each do |panel| %>
<%= panel %>
<% end %>
<% end %>
<% end %>

View file

@ -1,6 +1,6 @@
class TabsComponent < ViewComponent::Base
class DS::Tabs < DesignSystemComponent
renders_one :nav, ->(classes: nil) do
Tabs::NavComponent.new(
DS::Tabs::Nav.new(
active_tab: active_tab,
active_btn_classes: active_btn_classes,
inactive_btn_classes: inactive_btn_classes,
@ -13,7 +13,7 @@ class TabsComponent < ViewComponent::Base
content_tag(
:div,
class: ("hidden" unless tab_id == active_tab),
data: { id: tab_id, tabs_target: "panel" },
data: { id: tab_id, DS__tabs_target: "panel" },
&block
)
end

View file

@ -1,4 +1,4 @@
class Tabs::NavComponent < ViewComponent::Base
class DS::Tabs::Nav < DesignSystemComponent
erb_template <<~ERB
<%= tag.nav class: classes do %>
<% btns.each do |btn| %>
@ -12,7 +12,7 @@ class Tabs::NavComponent < ViewComponent::Base
:button, label, id: id,
type: "button",
class: class_names(btn_classes, id == active_tab ? active_btn_classes : inactive_btn_classes, classes),
data: { id: id, action: "tabs#show", tabs_target: "navBtn" },
data: { id: id, action: "DS--tabs#show", DS__tabs_target: "navBtn" },
&block
)
end

View file

@ -1,4 +1,4 @@
class Tabs::PanelComponent < ViewComponent::Base
class DS::Tabs::Panel < DesignSystemComponent
attr_reader :tab_id
def initialize(tab_id:)

View file

@ -1,4 +1,4 @@
class ToggleComponent < ViewComponent::Base
class DS::Toggle < DesignSystemComponent
attr_reader :id, :name, :checked, :disabled, :checked_value, :unchecked_value, :opts
def initialize(id:, name: nil, checked: false, disabled: false, checked_value: "1", unchecked_value: "0", **opts)

View file

@ -8,7 +8,7 @@
<div class="min-h-[800px]" data-testid="account-details">
<% if tabs.count > 1 %>
<%= render TabsComponent.new(active_tab: active_tab, url_param_key: "tab") do |tabs_container| %>
<%= render DS::Tabs.new(active_tab: active_tab, url_param_key: "tab") do |tabs_container| %>
<% tabs_container.with_nav(classes: "max-w-fit") do |nav| %>
<% tabs.each do |tab| %>
<% nav.with_btn(id: tab, label: tab.to_s.humanize, classes: "px-6") %>

View file

@ -0,0 +1,2 @@
class DesignSystemComponent < ViewComponent::Base
end

View file

@ -1,18 +0,0 @@
<%= tag.div data: {
controller: "tabs",
testid: testid,
tabs_session_key_value: session_key,
tabs_url_param_key_value: url_param_key,
tabs_nav_btn_active_class: active_btn_classes,
tabs_nav_btn_inactive_class: inactive_btn_classes
} do %>
<% if unstyled? %>
<%= content %>
<% else %>
<%= nav %>
<% panels.each do |panel| %>
<%= panel %>
<% end %>
<% end %>
<% end %>

View file

@ -21,7 +21,7 @@ module ApplicationHelper
if custom
inline_svg_tag("#{key}.svg", class: icon_classes, **opts)
elsif as_button
render ButtonComponent.new(variant: "icon", class: extra_classes, icon: key, size: size, type: "button", **opts)
render DS::Button.new(variant: "icon", class: extra_classes, icon: key, size: size, type: "button", **opts)
else
lucide_icon(key, class: icon_classes, **opts)
end

View file

@ -50,7 +50,7 @@ class StyledFormBuilder < ActionView::Helpers::FormBuilder
checked = object ? object.send(method) : options[:checked]
@template.render(
ToggleComponent.new(
DS::Toggle.new(
id: field_id,
name: field_name,
checked: checked,
@ -67,7 +67,7 @@ class StyledFormBuilder < ActionView::Helpers::FormBuilder
value ||= submit_default_value
@template.render(
ButtonComponent.new(
DS::Button.new(
text: value,
data: (options[:data] || {}).merge({ turbo_submits_with: "Submitting..." }),
full_width: true

View file

@ -41,7 +41,7 @@
<% end %>
<% if account.draft? %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Complete setup",
href: edit_account_path(account, return_to: return_to),
variant: :outline,
@ -49,7 +49,7 @@
) %>
<% elsif account.active? || account.disabled? %>
<%= form_with model: account, url: toggle_active_account_path(account), method: :patch, data: { turbo_frame: "_top", controller: "auto-submit-form" } do |f| %>
<%= render ToggleComponent.new(
<%= render DS::Toggle.new(
id: "account_#{account.id}_active",
name: "active",
checked: account.active?,

View file

@ -21,7 +21,7 @@
</details>
<% end %>
<%= render TabsComponent.new(active_tab: active_tab, session_key: "account_sidebar_tab", testid: "account-sidebar-tabs") do |tabs| %>
<%= render DS::Tabs.new(active_tab: active_tab, session_key: "account_sidebar_tab", testid: "account-sidebar-tabs") do |tabs| %>
<% tabs.with_nav do |nav| %>
<% nav.with_btn(id: "asset", label: "Assets") %>
<% nav.with_btn(id: "liability", label: "Debts") %>
@ -30,7 +30,7 @@
<% tabs.with_panel(tab_id: "asset") do %>
<div class="space-y-2">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New asset",
variant: "ghost",
href: new_account_path(step: "method_select", classification: "asset"),
@ -50,7 +50,7 @@
<% tabs.with_panel(tab_id: "liability") do %>
<div class="space-y-2">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New debt",
variant: "ghost",
href: new_account_path(step: "method_select", classification: "liability"),
@ -70,7 +70,7 @@
<% tabs.with_panel(tab_id: "all") do %>
<div class="space-y-2">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New account",
variant: "ghost",
full_width: true,

View file

@ -2,7 +2,7 @@
<%= link_to new_polymorphic_path(accountable, step: "method_select", return_to: params[:return_to]),
class: "flex items-center gap-4 w-full text-center focus:outline-hidden hover:bg-surface-hover focus:bg-surface-hover fg-primary border border-transparent block px-2 rounded-lg p-2" do %>
<%= render FilledIconComponent.new(
<%= render DS::FilledIcon.new(
icon: accountable.icon,
hex_color: accountable.color,
) %>

View file

@ -2,7 +2,7 @@
<div id="<%= account_group.dom_id(tab: all_tab ? :all : nil, mobile: mobile) %>">
<% is_open = open.nil? ? account_group.accounts.any? { |account| page_active?(account_path(account)) } : open %>
<%= render DisclosureComponent.new(align: :left, open: is_open) do |disclosure| %>
<%= render DS::Disclosure.new(align: :left, open: is_open) do |disclosure| %>
<% disclosure.with_summary_content do %>
<div class="flex items-center gap-3">
<%= icon "chevron-right", class: "group-open:transform group-open:rotate-90" %>
@ -51,7 +51,7 @@
</div>
<div class="my-2">
<%= render LinkComponent.new(
<%= render DS::Link.new(
href: new_polymorphic_path(account_group.key, step: "method_select"),
text: "New #{account_group.name.downcase.singularize}",
icon: "plus",

View file

@ -3,7 +3,7 @@
<%= tag.p t(".no_accounts"), class: "text-primary mb-1 font-medium" %>
<%= tag.p t(".empty_message"), class: "text-secondary mb-4" %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: t(".new_account"),
href: new_account_path,
frame: :modal

View file

@ -1,7 +1,7 @@
<%# locals: (account:, url:) %>
<% if @error_message.present? %>
<%= render AlertComponent.new(message: @error_message, variant: :error) %>
<%= render DS::Alert.new(message: @error_message, variant: :error) %>
<% end %>
<%= styled_form_with model: account, url: url, scope: :account, data: { turbo: false }, class: "flex flex-col gap-4 justify-between grow text-primary" do |form| %>

View file

@ -12,5 +12,5 @@
<% elsif account.logo.attached? %>
<%= image_tag account.logo, class: "shrink-0 rounded-full #{size_classes[size]}" %>
<% else %>
<%= render FilledIconComponent.new(variant: :text, hex_color: color || account.accountable.color, text: account.name, size: size, rounded: true) %>
<%= render DS::FilledIcon.new(variant: :text, hex_color: color || account.accountable.color, text: account.name, size: size, rounded: true) %>
<% end %>

View file

@ -2,7 +2,7 @@
<h1 class="text-xl"><%= t(".accounts") %></h1>
<div class="flex items-center gap-5">
<div class="flex items-center gap-2">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New account",
href: new_account_path(return_to: accounts_path),
variant: "primary",

View file

@ -25,7 +25,7 @@
<%= button_to imports_path(import: { type: "AccountImport" }),
data: { turbo_frame: :_top },
class: "flex items-center gap-4 w-full text-center focus:outline-hidden hover:bg-surface-hover focus:bg-surface-hover fg-primary border border-transparent block px-2 rounded-lg p-2" do %>
<%= render FilledIconComponent.new(
<%= render DS::FilledIcon.new(
icon: "download",
hex_color: "#F79009",
) %>

View file

@ -1,11 +1,11 @@
<%# locals: (title:, back_path: nil) %>
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<div class="flex flex-col relative" data-controller="list-keyboard-navigation">
<div class="border-b border-tertiary md:border-alpha-black-25 px-4 pb-4 text-gray-800 flex items-center justify-between gap-2">
<div class="flex items-center gap-2">
<% if back_path %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
variant: "icon",
icon: "arrow-left",
href: back_path,

View file

@ -5,7 +5,7 @@
<div class="flex items-center justify-between mb-4" data-testid="activity-menu">
<%= tag.h2 t(".title"), class: "font-medium text-lg" %>
<% unless @account.plaid_account_id.present? %>
<%= render MenuComponent.new(variant: "button") do |menu| %>
<%= render DS::Menu.new(variant: "button") do |menu| %>
<% menu.with_button(text: "New", variant: "secondary", icon: "plus") %>
<% menu.with_item(

View file

@ -10,7 +10,7 @@
<div class="flex items-center gap-3">
<h2 class="font-medium text-xl truncate <%= "animate-pulse" if account.syncing? %>"><%= title %></h2>
<% if account.draft? %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Complete setup",
href: edit_account_path(account),
variant: :outline,

View file

@ -1,6 +1,6 @@
<%# locals: (account:) %>
<%= render MenuComponent.new(testid: "account-menu") do |menu| %>
<%= render DS::Menu.new(testid: "account-menu") do |menu| %>
<% menu.with_item(variant: "link", text: "Edit", href: edit_account_path(account), icon: "pencil-line", data: { turbo_frame: :modal }) %>
<% unless account.crypto? %>

View file

@ -12,7 +12,7 @@
<% if budget_category.category.lucide_icon %>
<%= icon(budget_category.category.lucide_icon, color: "current") %>
<% else %>
<%= render FilledIconComponent.new(
<%= render DS::FilledIcon.new(
variant: :text,
hex_color: budget_category.category.color,
text: budget_category.category.name,

View file

@ -1,5 +1,5 @@
<div id="<%= dom_id(budget, :confirm_button) %>">
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: "Confirm",
variant: "primary",
full_width: true,

View file

@ -6,12 +6,12 @@
</p>
<div class="flex items-center gap-2">
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: "Use defaults (recommended)",
href: bootstrap_categories_path,
) %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New category",
variant: "outline",
icon: "plus",

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new(variant: :drawer) do |dialog| %>
<%= render DS::Dialog.new(variant: :drawer) do |dialog| %>
<% dialog.with_header do %>
<div>
<p class="text-sm text-secondary">Category</p>
@ -119,7 +119,7 @@
<% end %>
</ul>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "View all category transactions",
variant: "outline",
full_width: true,

View file

@ -12,7 +12,7 @@
<%= format_money(budget.actual_spending_money) %>
</div>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "of #{budget.budgeted_spending_money.format}",
variant: "secondary",
icon: "pencil",
@ -25,7 +25,7 @@
<span><%= format_money Money.new(0, budget.currency || budget.family.currency) %></span>
</div>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New budget",
size: "sm",
icon: "plus",
@ -46,7 +46,7 @@
<%= format_money(bc.actual_spending_money) %>
</p>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "of #{bc.budgeted_spending_money.format(precision: 0)}",
variant: "secondary",
icon: "pencil",

View file

@ -3,7 +3,7 @@
<div class="flex items-center gap-1 mb-4">
<div class="flex items-center gap-2">
<% if budget.previous_budget_param %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
variant: "icon",
icon: "chevron-left",
href: budget_path(budget.previous_budget_param),
@ -15,7 +15,7 @@
<% end %>
<% if budget.next_budget_param %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
variant: "icon",
icon: "chevron-right",
href: budget_path(budget.next_budget_param),
@ -27,7 +27,7 @@
<% end %>
</div>
<%= render MenuComponent.new(variant: "button") do |menu| %>
<%= render DS::Menu.new(variant: "button") do |menu| %>
<% menu.with_button class: "flex items-center gap-1 hover:bg-alpha-black-25 cursor-pointer rounded-md p-2" do %>
<span class="text-primary font-medium text-lg lg:text-base"><%= @budget.name %></span>
<%= icon("chevron-down") %>
@ -39,7 +39,7 @@
<% end %>
<div class="ml-auto">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Today",
variant: "outline",
href: budget_path(Budget.date_to_param(Date.current)),

View file

@ -4,7 +4,7 @@
<%= icon "alert-triangle", size: "lg", color: "destructive" %>
<p class="text-secondary text-sm text-center">You have over-allocated your budget. Please fix your allocations.</p>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Fix allocations",
variant: "secondary",
size: "sm",

View file

@ -38,7 +38,7 @@
<% param_key = Budget.date_to_param(date) %>
<% if Budget.budget_date_valid?(date, family: family) %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
variant: "ghost",
text: month_name,
href: budget_path(param_key),

View file

@ -29,7 +29,7 @@
</p>
</div>
<%= render ToggleComponent.new(
<%= render DS::Toggle.new(
id: "auto_fill",
data: {
action: "change->budget-form#toggleAutoFill",

View file

@ -18,7 +18,7 @@
<div>
<% if @budget.initialized? && @budget.available_to_allocate.positive? %>
<%= render TabsComponent.new(active_tab: params[:tab].presence || "budgeted") do |tabs| %>
<%= render DS::Tabs.new(active_tab: params[:tab].presence || "budgeted") do |tabs| %>
<% tabs.with_nav do |nav| %>
<% nav.with_btn(id: "budgeted", label: "Budgeted") %>
<% nav.with_btn(id: "actuals", label: "Actual") %>
@ -49,7 +49,7 @@
<h2 class="text-lg font-medium">Categories</h2>
<% if @budget.initialized? %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Edit",
variant: "secondary",
icon: "settings-2",

View file

@ -12,7 +12,7 @@
</div>
<div class="justify-self-end">
<%= render MenuComponent.new do |menu| %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(variant: "link", text: t(".edit"), icon: "pencil", href: edit_category_path(category), data: { turbo_frame: :modal }) %>
<% if category.transactions.any? %>

View file

@ -1,6 +1,6 @@
<%# locals: (transaction:) %>
<%= render MenuComponent.new(variant: "button") do |menu| %>
<%= render DS::Menu.new(variant: "button") do |menu| %>
<% menu.with_button do %>
<% render partial: "categories/badge", locals: { category: transaction.category } %>
<% end %>

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".edit")) %>
<% dialog.with_body do %>
<%= render "form", category: @category, categories: @categories %>

View file

@ -2,7 +2,7 @@
<h1 class="text-primary text-xl font-medium"><%= t(".categories") %></h1>
<div class="flex items-center gap-2">
<%= render MenuComponent.new do |menu| %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
text: "Delete all",
@ -12,7 +12,7 @@
confirm: CustomConfirm.for_resource_deletion("all categories", high_severity: true)) %>
<% end %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
icon: "plus",
@ -38,12 +38,12 @@
<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">
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: t(".bootstrap"),
href: bootstrap_categories_path,
) %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: t(".new"),
variant: "outline",
icon: "plus",

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".new_category")) %>
<% dialog.with_body do %>
<%= render "form", category: @category, categories: @categories %>

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".delete_category"), subtitle: t(".explanation", category_name: @category.name)) %>
<% dialog.with_body do %>
@ -14,14 +14,14 @@
{ prompt: t(".replacement_category_prompt"), label: t(".category"), container_class: "mb-4" },
data: { deletion_target: "replacementField", action: "deletion#chooseSubmitButton" } %>
<%= render ButtonComponent.new(
<%= render DS::Button.new(
variant: "destructive",
text: t(".delete_and_leave_uncategorized", category_name: @category.name),
full_width: true,
data: { deletion_target: "destructiveSubmitButton" }
) %>
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: "Delete and reassign",
data: { deletion_target: "safeSubmitButton" },
hidden: true,

View file

@ -24,7 +24,7 @@
<%= render partial: "categories/badge", locals: { category: category } %>
<% end %>
<%= render MenuComponent.new do |menu| %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(variant: "link", text: t(".edit"), icon: "pencil-line", href: edit_category_path(category), data: { turbo_frame: :modal }) %>
<% menu.with_item(variant: "link", text: t(".delete"), icon: "trash-2", href: new_category_deletion_path(category), data: { turbo_frame: :modal }, destructive: true) %>
<% end %>

View file

@ -29,7 +29,7 @@
<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>
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: t(".bootstrap"),
variant: "outline",
href: bootstrap_categories_path,

View file

@ -11,7 +11,7 @@
</p>
</div>
<%= render MenuComponent.new(icon_vertical: true) do |menu| %>
<%= render DS::Menu.new(icon_vertical: true) do |menu| %>
<% menu.with_item(
variant: "link",
text: "Edit chat title",

View file

@ -4,7 +4,7 @@
<% path = chat.new_record? ? chats_path : chats_path(previous_chat_id: chat.id) %>
<div class="flex items-center gap-2 grow">
<%= render LinkComponent.new(
<%= render DS::Link.new(
id: "chat-nav-back",
variant: "icon",
icon: "menu",
@ -18,7 +18,7 @@
</div>
</div>
<%= render MenuComponent.new(icon_vertical: true) do |menu| %>
<%= render DS::Menu.new(icon_vertical: true) do |menu| %>
<% menu.with_item(variant: "link", text: "Start new chat", href: new_chat_path, icon: "plus") %>
<% unless chat.new_record? %>

View file

@ -10,7 +10,7 @@
<div class="flex items-center justify-between gap-2">
<p class="text-xs text-red-500">Failed to generate response. Please try again.</p>
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: "Retry",
href: retry_chat_path(chat),
) %>

View file

@ -5,7 +5,7 @@
<div class="grow flex flex-col">
<div class="flex items-center justify-between my-6">
<h1 class="text-xl font-medium">Chats</h1>
<%= render LinkComponent.new(
<%= render DS::Link.new(
id: "new-chat",
icon: "plus",
variant: "icon",

View file

@ -27,7 +27,7 @@
</div>
<div class="flex justify-center py-8">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Edit account details",
variant: "ghost",
href: edit_credit_card_path(account),

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".edit", account: @account.name)) %>
<% dialog.with_body do %>

View file

@ -5,7 +5,7 @@
show_eu_link: @show_eu_link,
accountable_type: "CreditCard" %>
<% else %>
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title")) %>
<% dialog.with_body do %>
<%= render "credit_cards/form", account: @account, url: credit_cards_path %>

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".edit", account: @account.name)) %>
<% dialog.with_body do %>
<%= render "form", account: @account, url: crypto_path(@account) %>

View file

@ -5,7 +5,7 @@
show_eu_link: @show_eu_link,
accountable_type: "Crypto" %>
<% else %>
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title")) %>
<% dialog.with_body do %>
<%= render "form", account: @account, url: cryptos_path %>

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".edit", account: @account.name)) %>
<% dialog.with_body do %>
<%= render "form", account: @account, url: depository_path(@account) %>

View file

@ -5,7 +5,7 @@
show_eu_link: @show_eu_link,
accountable_type: "Depository" %>
<% else %>
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title")) %>
<% dialog.with_body do %>
<%= render "depositories/form", account: @account, url: depositories_path %>

View file

@ -13,7 +13,7 @@
</div>
<div class="text-center">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Go back",
href: "javascript:history.back()",
variant: :secondary

View file

@ -37,7 +37,7 @@
<% if params[:display].present? %>
<%= hidden_field_tag :display, params[:display], id: nil %>
<% end %>
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: t("doorkeeper.authorizations.buttons.authorize"),
variant: :primary,
size: :lg,
@ -59,7 +59,7 @@
<% if params[:display].present? %>
<%= hidden_field_tag :display, params[:display], id: nil %>
<% end %>
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: t("doorkeeper.authorizations.buttons.deny"),
variant: :outline,
size: :lg,

View file

@ -15,7 +15,7 @@
</p>
</div>
<div class="justify-self-end">
<%= render MenuComponent.new do |menu| %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(variant: "link", text: "Edit", href: edit_family_merchant_path(family_merchant), icon: "pencil", data: { turbo_frame: "modal" }) %>
<% menu.with_item(
variant: "button",

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title")) %>
<% dialog.with_body do %>
<%= render "form", family_merchant: @family_merchant %>

View file

@ -1,7 +1,7 @@
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium">Merchants</h1>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New merchant",
variant: "primary",
href: new_family_merchant_path,
@ -29,7 +29,7 @@
<div class="text-center flex flex-col items-center max-w-[300px]">
<p class="text-primary mb-1 font-medium text-sm"><%= t(".empty") %></p>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: t(".new"),
icon: "plus",
href: new_family_merchant_path,

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title")) %>
<% dialog.with_body do %>
<%= render "form", family_merchant: @family_merchant %>

View file

@ -4,7 +4,7 @@
<div class="grid grid-cols-12 items-center text-primary text-sm font-medium p-4">
<div class="col-span-4 flex items-center gap-4">
<%= render FilledIconComponent.new(
<%= render DS::FilledIcon.new(
variant: :text,
text: currency.symbol,
rounded: true,

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new(variant: "drawer") do |dialog| %>
<%= render DS::Dialog.new(variant: "drawer") do |dialog| %>
<% dialog.with_header do %>
<div class="flex items-center justify-between">
<div>

View file

@ -17,7 +17,7 @@
<p class="text-success text-sm">Your data has been cleaned</p>
</div>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Next step",
variant: "primary",
href: import_confirm_path(@import),

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">
<%= render LinkComponent.new(text: "Manually configure", href: import_configuration_path(@import), variant: "outline") %>
<%= render ButtonComponent.new(text: "Apply template", href: apply_template_import_path(@import), method: :put, data: { turbo_frame: :_top }) %>
<%= render DS::Link.new(text: "Manually configure", href: import_configuration_path(@import), variant: "outline") %>
<%= render DS::Button.new(text: "Apply template", href: apply_template_import_path(@import), method: :put, data: { turbo_frame: :_top }) %>
</div>
</div>
</div>

View file

@ -11,7 +11,7 @@
<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" %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Create account",
variant: "primary",
href: new_account_path(return_to: import_confirm_path(import)),
@ -25,7 +25,7 @@
<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 mx-auto">
<%= tag.p t(".unassigned_account"), class: "text-sm" %>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: t(".create_account"),
variant: "primary",
href: new_account_path(return_to: import_confirm_path(import)),
@ -59,7 +59,7 @@
</div>
<div class="flex justify-center w-full">
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Next",
variant: "primary",
href: is_last_step ? import_path(import) : url_for(step: step_idx + 2),

View file

@ -11,7 +11,7 @@
<p class="text-secondary text-sm"><%= t(".description") %></p>
</div>
<%= render TabsComponent.new(active_tab: params[:tab] || "csv-upload", url_param_key: "tab", testid: "import-tabs") do |tabs| %>
<%= render DS::Tabs.new(active_tab: params[:tab] || "csv-upload", url_param_key: "tab", testid: "import-tabs") do |tabs| %>
<% tabs.with_nav do |nav| %>
<% nav.with_btn(id: "csv-upload", label: "Upload CSV") %>
<% nav.with_btn(id: "csv-paste", label: "Copy & Paste") %>

View file

@ -2,7 +2,7 @@
<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>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
href: new_import_path,

View file

@ -11,6 +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>
<%= render ButtonComponent.new(text: "Try again", href: publish_import_path(import), full_width: true) %>
<%= render DS::Button.new(text: "Try again", href: publish_import_path(import), full_width: true) %>
</div>
</div>

View file

@ -36,7 +36,7 @@
<% end %>
</div>
<%= render MenuComponent.new do |menu| %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(variant: "link", text: t(".view"), href: import_path(import), icon: "eye") %>
<% if import.complete? || import.revert_failed? %>

View file

@ -12,8 +12,8 @@
</div>
<div class="space-y-2 flex flex-col">
<%= render LinkComponent.new(text: "Check status", href: import_path(import), variant: "primary", full_width: true) %>
<%= render LinkComponent.new(text: "Back to dashboard", href: root_path, variant: "secondary", full_width: true) %>
<%= render DS::Link.new(text: "Check status", href: import_path(import), variant: "primary", full_width: true) %>
<%= render DS::Link.new(text: "Back to dashboard", href: root_path, variant: "secondary", full_width: true) %>
</div>
</div>
</div>

View file

@ -35,5 +35,5 @@
</div>
</div>
<%= render ButtonComponent.new(text: "Publish import", href: publish_import_path(import), full_width: true) %>
<%= render DS::Button.new(text: "Publish import", href: publish_import_path(import), full_width: true) %>
</div>

View file

@ -11,7 +11,7 @@
<p class="text-sm text-secondary">Please try again or contact support.</p>
</div>
<%= render ButtonComponent.new(
<%= render DS::Button.new(
text: "Try again",
full_width: true,
href: revert_import_path(import)

View file

@ -11,7 +11,7 @@
<p class="text-sm text-secondary">Your imported data has been successfully added to the app and is now ready for use.</p>
</div>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Back to dashboard",
variant: "primary",
full_width: true,

View file

@ -1,7 +1,7 @@
<div class="flex items-center justify-between">
<h1 class="text-xl font-medium text-primary"><%= t(".title") %></h1>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "New import",
href: new_import_path,
icon: "plus",

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title"), subtitle: t(".description")) %>
<% dialog.with_body do %>

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".edit", account: @account.name)) %>
<% dialog.with_body do %>
<%= render "investments/form", account: @account, url: investment_path(@account) %>

View file

@ -5,7 +5,7 @@
show_eu_link: @show_eu_link,
accountable_type: "Investment" %>
<% else %>
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title")) %>
<% dialog.with_body do %>
<%= render "investments/form", account: @account, url: investments_path %>

View file

@ -1,4 +1,4 @@
<%= render DialogComponent.new do |dialog| %>
<%= render DS::Dialog.new do |dialog| %>
<% dialog.with_header(title: t(".title"), subtitle: t(".subtitle")) %>
<% dialog.with_body do %>

View file

@ -60,7 +60,7 @@
</ul>
<div class="pl-2 mt-auto mx-auto flex flex-col gap-2">
<%= render ButtonComponent.new(
<%= render DS::Button.new(
variant: "icon",
icon: "message-circle-question",
data: { action: "intercom#show" }
@ -93,7 +93,7 @@
<p class="text-sm text-secondary"><%= Current.family.days_left_in_trial %> days remaining</p>
</div>
<%= render LinkComponent.new(
<%= render DS::Link.new(
text: "Upgrade",
href: upgrade_subscription_path,
) %>

View file

@ -1,7 +1,7 @@
<%= render "layouts/shared/htmldoc" do %>
<div class="flex flex-col h-full bg-container">
<header class="flex items-center justify-between p-8">
<%= render LinkComponent.new(
<%= render DS::Link.new(
variant: "icon",
icon: "arrow-left",
href: content_for(:previous_path) || imports_path
@ -11,7 +11,7 @@
<%= yield :header_nav %>
</nav>
<%= render LinkComponent.new(
<%= render DS::Link.new(
variant: "icon",
icon: "x",
href: imports_path

Some files were not shown because too many files have changed in this diff Show more