From 106c8f8780ad94974d0aa2b6f13672315e4a3e53 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 19 Jun 2025 18:37:32 +0200 Subject: [PATCH 01/16] Create custom form builder This will allow to clean up existing forms and write more concise forms in the future. Custom form builder encapsulates all the calls to "render", "component()" initializations, and assumes some defaults, e.g. if "checked" is not passed to "f.checkbox" we get it by calling @object.send(method) --- .../solidus_admin/base_component.rb | 1 + .../solidus_admin/solidus_form_helper.rb | 9 ++++ admin/app/lib/solidus_admin/form_builder.rb | 50 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 admin/app/helpers/solidus_admin/solidus_form_helper.rb create mode 100644 admin/app/lib/solidus_admin/form_builder.rb diff --git a/admin/app/components/solidus_admin/base_component.rb b/admin/app/components/solidus_admin/base_component.rb index e39c1707a4b..37daa9fea48 100644 --- a/admin/app/components/solidus_admin/base_component.rb +++ b/admin/app/components/solidus_admin/base_component.rb @@ -9,6 +9,7 @@ class BaseComponent < ViewComponent::Base include SolidusAdmin::ComponentsHelper include SolidusAdmin::StimulusHelper include SolidusAdmin::VoidElementsHelper + include SolidusAdmin::SolidusFormHelper include Turbo::FramesHelper def icon_tag(name, **attrs) diff --git a/admin/app/helpers/solidus_admin/solidus_form_helper.rb b/admin/app/helpers/solidus_admin/solidus_form_helper.rb new file mode 100644 index 00000000000..c4a335ba930 --- /dev/null +++ b/admin/app/helpers/solidus_admin/solidus_form_helper.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module SolidusAdmin + module SolidusFormHelper + def solidus_form_for(*args, **kwargs, &block) + form_for(*args, **kwargs, builder: SolidusAdmin::FormBuilder, &block) + end + end +end diff --git a/admin/app/lib/solidus_admin/form_builder.rb b/admin/app/lib/solidus_admin/form_builder.rb new file mode 100644 index 00000000000..77646863e51 --- /dev/null +++ b/admin/app/lib/solidus_admin/form_builder.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class SolidusAdmin::FormBuilder < ActionView::Helpers::FormBuilder + include SolidusAdmin::ComponentsHelper + + delegate :render, to: :@template + + def text_field(method, **options) + render component("ui/forms/field").text_field(self, method, **options) + end + + def text_area(method, **options) + render component("ui/forms/field").text_area(self, method, **options) + end + + def select(method, choices, **options) + render component("ui/forms/field").select(self, method, choices, **options) + end + + def checkbox(method, checked: nil, **options, &block) + checked = checked.nil? ? @object.public_send(method) : checked + component_instance = component("ui/forms/checkbox").new(object_name: @object_name, checked:, method:, **options) + render component_instance, &block + end + + def checkbox_row(method, options:, row_title:, **attrs) + render component("ui/checkbox_row").new(form: self, method:, options:, row_title:, **attrs) + end + + def input(method, **options) + name = "#{@object_name}[#{method}]" + value = @object.public_send(method) if options[:value].nil? + render component("ui/forms/input").new(name:, value:, **options) + end + + def hidden_field(method, **options) + input(method, type: :hidden, autocomplete: "off", **options) + end + + def switch_field(method, label:, **options) + name = "#{@object_name}[#{method}]" + error = @object.errors[method] + checked = @object.public_send(method) + render component("ui/forms/switch_field").new(label:, name:, error:, checked:, **options) + end + + def submit(text, **options) + render component("ui/button").new(type: :submit, text:, form: id, **options) + end +end From 71861e5f973151f4162d935ee168f4872b013f07 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 17:30:39 +0200 Subject: [PATCH 02/16] Update builder#switch_field to expect default label --- admin/app/lib/solidus_admin/form_builder.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/admin/app/lib/solidus_admin/form_builder.rb b/admin/app/lib/solidus_admin/form_builder.rb index 77646863e51..a9fefb9f98c 100644 --- a/admin/app/lib/solidus_admin/form_builder.rb +++ b/admin/app/lib/solidus_admin/form_builder.rb @@ -37,7 +37,8 @@ def hidden_field(method, **options) input(method, type: :hidden, autocomplete: "off", **options) end - def switch_field(method, label:, **options) + def switch_field(method, label: nil, **options) + label = @object.class.human_attribute_name(method) if label.nil? name = "#{@object_name}[#{method}]" error = @object.errors[method] checked = @object.public_send(method) From 747d4e08f4c2a7a3eb210d63964e4bfc6c506f95 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 19:33:11 +0200 Subject: [PATCH 03/16] Assume switch field wants to include hidden by default --- admin/app/lib/solidus_admin/form_builder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/app/lib/solidus_admin/form_builder.rb b/admin/app/lib/solidus_admin/form_builder.rb index a9fefb9f98c..2293f8c5748 100644 --- a/admin/app/lib/solidus_admin/form_builder.rb +++ b/admin/app/lib/solidus_admin/form_builder.rb @@ -37,12 +37,12 @@ def hidden_field(method, **options) input(method, type: :hidden, autocomplete: "off", **options) end - def switch_field(method, label: nil, **options) + def switch_field(method, label: nil, include_hidden: true, **options) label = @object.class.human_attribute_name(method) if label.nil? name = "#{@object_name}[#{method}]" error = @object.errors[method] checked = @object.public_send(method) - render component("ui/forms/switch_field").new(label:, name:, error:, checked:, **options) + render component("ui/forms/switch_field").new(label:, name:, error:, checked:, include_hidden:, **options) end def submit(text, **options) From f4293a382001ccb4f952108cb821ccf0e3b4c7a3 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Wed, 25 Jun 2025 19:24:48 +0200 Subject: [PATCH 04/16] Add missing "Add new" button for tax rates page --- admin/app/components/solidus_admin/tax_rates/index/component.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/app/components/solidus_admin/tax_rates/index/component.rb b/admin/app/components/solidus_admin/tax_rates/index/component.rb index 148d280a682..9988fbaf264 100644 --- a/admin/app/components/solidus_admin/tax_rates/index/component.rb +++ b/admin/app/components/solidus_admin/tax_rates/index/component.rb @@ -13,7 +13,7 @@ def search_url solidus_admin.tax_rates_path end - def actions + def page_actions render component("ui/button").new( tag: :a, text: t('.add'), From 351842b2c8f0c01b4c317f78df2239db56864f16 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 15:16:12 +0200 Subject: [PATCH 05/16] TaxRates controller inherits from ResourcesController --- .../solidus_admin/tax_rates_controller.rb | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/admin/app/controllers/solidus_admin/tax_rates_controller.rb b/admin/app/controllers/solidus_admin/tax_rates_controller.rb index 75c2f239eb4..ae36cf09edc 100644 --- a/admin/app/controllers/solidus_admin/tax_rates_controller.rb +++ b/admin/app/controllers/solidus_admin/tax_rates_controller.rb @@ -1,35 +1,16 @@ # frozen_string_literal: true module SolidusAdmin - class TaxRatesController < SolidusAdmin::BaseController - include SolidusAdmin::ControllerHelpers::Search - - def index - tax_rates = apply_search_to( - Spree::TaxRate.order(created_at: :desc, id: :desc), - param: :q, - ) - - set_page_and_extract_portion_from(tax_rates) - - respond_to do |format| - format.html { render component('tax_rates/index').new(page: @page) } - end - end - - def destroy - @tax_rates = Spree::TaxRate.where(id: params[:id]) - - Spree::TaxRate.transaction { @tax_rates.destroy_all } + class TaxRatesController < SolidusAdmin::ResourcesController + private - flash[:notice] = t('.success') - redirect_back_or_to tax_rates_path, status: :see_other - end + def resource_class = Spree::TaxRate - private + def resources_sorting_options = { created_at: :desc, id: :desc } - def tax_rate_params - params.require(:tax_rate).permit(:tax_rate_id, permitted_tax_rate_attributes) + def permitted_resource_params + params.require(:tax_rate).permit(:name, :zone_id, :show_rate_in_label, :calculator_type, :amount, :level, + :included_in_price, :starts_at, :expires_at, tax_category_ids: []) end end end From 3378cdc7f4c017b6bb6639d7e4cabeaa750fa6fb Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 15:22:29 +0200 Subject: [PATCH 06/16] Update tax rates index component Display table cells as links to edit tax rate. Since "zone" is an optional association, added support when zone is not present on the record. --- .../tax_rates/index/component.rb | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/admin/app/components/solidus_admin/tax_rates/index/component.rb b/admin/app/components/solidus_admin/tax_rates/index/component.rb index 9988fbaf264..bcd9ca2e221 100644 --- a/admin/app/components/solidus_admin/tax_rates/index/component.rb +++ b/admin/app/components/solidus_admin/tax_rates/index/component.rb @@ -59,16 +59,27 @@ def columns [ { header: :zone, - data: -> { _1.zone&.name }, + data: ->(tax_rate) do + link_to tax_rate.zone.name, row_url(tax_rate), class: 'body-link' if tax_rate.zone.present? + end + }, + { + header: :name, + data: ->(tax_rate) do + link_to tax_rate.name, row_url(tax_rate), class: 'body-link' + end }, - :name, { header: :tax_categories, - data: -> { _1.tax_categories.map(&:name).join(', ') }, + data: ->(tax_rate) do + link_to tax_rate.tax_categories.map(&:name).join(', '), row_url(tax_rate), class: 'body-link' + end }, { header: :amount, - data: -> { _1.display_amount }, + data: ->(tax_rate) do + link_to tax_rate.display_amount, row_url(tax_rate), class: 'body-link' + end }, { header: :included_in_price, @@ -78,10 +89,17 @@ def columns header: :show_rate_in_label, data: -> { _1.show_rate_in_label? ? component('ui/badge').yes : component('ui/badge').no }, }, - :expires_at, + { + header: :expires_at, + data: ->(tax_rate) do + link_to tax_rate.expires_at.to_date, row_url(tax_rate), class: 'body-link' if tax_rate.expires_at + end + }, { header: Spree::Calculator.model_name.human, - data: -> { _1.calculator&.class&.model_name&.human } + data: ->(tax_rate) do + link_to tax_rate.calculator&.class&.model_name&.human, row_url(tax_rate), class: 'body-link' + end }, ] end From 57a40f57ed33e3ec536061ebf51e544849de0ea1 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 15:30:15 +0200 Subject: [PATCH 07/16] Add component for new tax rate page --- .../tax_rates/new/component.html.erb | 17 +++++++++++++++++ .../solidus_admin/tax_rates/new/component.rb | 5 +++++ .../solidus_admin/tax_rates/new/component.yml | 4 ++++ 3 files changed, 26 insertions(+) create mode 100644 admin/app/components/solidus_admin/tax_rates/new/component.html.erb create mode 100644 admin/app/components/solidus_admin/tax_rates/new/component.rb create mode 100644 admin/app/components/solidus_admin/tax_rates/new/component.yml diff --git a/admin/app/components/solidus_admin/tax_rates/new/component.html.erb b/admin/app/components/solidus_admin/tax_rates/new/component.html.erb new file mode 100644 index 00000000000..53c048bd4c4 --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/new/component.html.erb @@ -0,0 +1,17 @@ + + +<%= page id: :resource_modal do %> + <%= page_header do %> + <%= page_header_back(solidus_admin.tax_rates_path) %> + <%= page_header_title(t(".title")) %> + <%= page_header_actions do %> + <%= render component("ui/button").new( + tag: :a, + text: t(".discard"), + href: solidus_admin.tax_rates_path, + scheme: :secondary + ) %> + <%= render component("ui/button").new(tag: :button, text: t(".save"), form: form_id) %> + <% end %> + <% end %> +<% end %> diff --git a/admin/app/components/solidus_admin/tax_rates/new/component.rb b/admin/app/components/solidus_admin/tax_rates/new/component.rb new file mode 100644 index 00000000000..f936c3e0fb7 --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/new/component.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class SolidusAdmin::TaxRates::New::Component < SolidusAdmin::Resources::New::Component + include SolidusAdmin::Layout::PageHelpers +end diff --git a/admin/app/components/solidus_admin/tax_rates/new/component.yml b/admin/app/components/solidus_admin/tax_rates/new/component.yml new file mode 100644 index 00000000000..e29bdc5a8d2 --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/new/component.yml @@ -0,0 +1,4 @@ +en: + discard: "Discard" + save: "Save" + title: "New Tax Rate" From 12e2698cd345f9f7c392ccac26b324c314cf8c49 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 15:32:40 +0200 Subject: [PATCH 08/16] Add component for edit tax rate page --- .../tax_rates/edit/component.html.erb | 17 +++++++++++++++++ .../solidus_admin/tax_rates/edit/component.rb | 5 +++++ .../solidus_admin/tax_rates/edit/component.yml | 4 ++++ 3 files changed, 26 insertions(+) create mode 100644 admin/app/components/solidus_admin/tax_rates/edit/component.html.erb create mode 100644 admin/app/components/solidus_admin/tax_rates/edit/component.rb create mode 100644 admin/app/components/solidus_admin/tax_rates/edit/component.yml diff --git a/admin/app/components/solidus_admin/tax_rates/edit/component.html.erb b/admin/app/components/solidus_admin/tax_rates/edit/component.html.erb new file mode 100644 index 00000000000..53c048bd4c4 --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/edit/component.html.erb @@ -0,0 +1,17 @@ + + +<%= page id: :resource_modal do %> + <%= page_header do %> + <%= page_header_back(solidus_admin.tax_rates_path) %> + <%= page_header_title(t(".title")) %> + <%= page_header_actions do %> + <%= render component("ui/button").new( + tag: :a, + text: t(".discard"), + href: solidus_admin.tax_rates_path, + scheme: :secondary + ) %> + <%= render component("ui/button").new(tag: :button, text: t(".save"), form: form_id) %> + <% end %> + <% end %> +<% end %> diff --git a/admin/app/components/solidus_admin/tax_rates/edit/component.rb b/admin/app/components/solidus_admin/tax_rates/edit/component.rb new file mode 100644 index 00000000000..583e927f6fd --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/edit/component.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class SolidusAdmin::TaxRates::Edit::Component < SolidusAdmin::Resources::Edit::Component + include SolidusAdmin::Layout::PageHelpers +end diff --git a/admin/app/components/solidus_admin/tax_rates/edit/component.yml b/admin/app/components/solidus_admin/tax_rates/edit/component.yml new file mode 100644 index 00000000000..e733a6052d8 --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/edit/component.yml @@ -0,0 +1,4 @@ +en: + discard: "Discard" + save: "Save" + title: "Edit Tax Rate" From 4ed11ef9780c1898cc06c57f6698d50e146a0a70 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 15:35:27 +0200 Subject: [PATCH 09/16] Update tax rate routes --- admin/config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/config/routes.rb b/admin/config/routes.rb index ec215522965..125b1544f5e 100644 --- a/admin/config/routes.rb +++ b/admin/config/routes.rb @@ -73,7 +73,7 @@ admin_resources :taxonomies, only: [:index, :destroy], sortable: true admin_resources :promotion_categories, only: [:index, :destroy] admin_resources :tax_categories, except: [:show] - admin_resources :tax_rates, only: [:index, :destroy] + admin_resources :tax_rates, except: [:show] admin_resources :payment_methods, only: [:index, :destroy], sortable: true admin_resources :stock_items, only: [:index, :edit, :update] admin_resources :shipping_methods, only: [:index, :destroy] From 962511ad959e5d2a11b13aea9062e1c7ca955ec6 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 15:45:28 +0200 Subject: [PATCH 10/16] Update tax rate index component Put relevant paths for solidus admin pages --- .../solidus_admin/tax_rates/index/component.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/admin/app/components/solidus_admin/tax_rates/index/component.rb b/admin/app/components/solidus_admin/tax_rates/index/component.rb index bcd9ca2e221..bad513f4982 100644 --- a/admin/app/components/solidus_admin/tax_rates/index/component.rb +++ b/admin/app/components/solidus_admin/tax_rates/index/component.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class SolidusAdmin::TaxRates::Index::Component < SolidusAdmin::Taxes::Component - def row_url(tax_rate) - spree.edit_admin_tax_rate_path(tax_rate) + def edit_path(tax_rate) + solidus_admin.edit_tax_rate_path(tax_rate) end def model_class @@ -17,7 +17,7 @@ def page_actions render component("ui/button").new( tag: :a, text: t('.add'), - href: spree.new_admin_tax_rate_path, + href: solidus_admin.new_tax_rate_path, icon: "add-line", class: "align-self-end w-full", ) @@ -60,25 +60,25 @@ def columns { header: :zone, data: ->(tax_rate) do - link_to tax_rate.zone.name, row_url(tax_rate), class: 'body-link' if tax_rate.zone.present? + link_to tax_rate.zone.name, edit_path(tax_rate), class: 'body-link' if tax_rate.zone.present? end }, { header: :name, data: ->(tax_rate) do - link_to tax_rate.name, row_url(tax_rate), class: 'body-link' + link_to tax_rate.name, edit_path(tax_rate), class: 'body-link' end }, { header: :tax_categories, data: ->(tax_rate) do - link_to tax_rate.tax_categories.map(&:name).join(', '), row_url(tax_rate), class: 'body-link' + link_to tax_rate.tax_categories.map(&:name).join(', '), edit_path(tax_rate), class: 'body-link' end }, { header: :amount, data: ->(tax_rate) do - link_to tax_rate.display_amount, row_url(tax_rate), class: 'body-link' + link_to tax_rate.display_amount, edit_path(tax_rate), class: 'body-link' end }, { @@ -92,13 +92,13 @@ def columns { header: :expires_at, data: ->(tax_rate) do - link_to tax_rate.expires_at.to_date, row_url(tax_rate), class: 'body-link' if tax_rate.expires_at + link_to tax_rate.expires_at.to_date, edit_path(tax_rate), class: 'body-link' if tax_rate.expires_at end }, { header: Spree::Calculator.model_name.human, data: ->(tax_rate) do - link_to tax_rate.calculator&.class&.model_name&.human, row_url(tax_rate), class: 'body-link' + link_to tax_rate.calculator&.class&.model_name&.human, edit_path(tax_rate), class: 'body-link' end }, ] From bc9cad1fa1c4f8aec5fc56b578bf1618b3c0b86d Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 19:43:29 +0200 Subject: [PATCH 11/16] Add component with tax rate form --- .../tax_rates/form/component.html.erb | 25 ++++++++++++++++ .../solidus_admin/tax_rates/form/component.rb | 29 +++++++++++++++++++ .../tax_rates/form/component.yml | 9 ++++++ core/config/locales/en.yml | 3 ++ 4 files changed, 66 insertions(+) create mode 100644 admin/app/components/solidus_admin/tax_rates/form/component.html.erb create mode 100644 admin/app/components/solidus_admin/tax_rates/form/component.rb create mode 100644 admin/app/components/solidus_admin/tax_rates/form/component.yml diff --git a/admin/app/components/solidus_admin/tax_rates/form/component.html.erb b/admin/app/components/solidus_admin/tax_rates/form/component.html.erb new file mode 100644 index 00000000000..4138434efdf --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/form/component.html.erb @@ -0,0 +1,25 @@ +<%= solidus_form_for @tax_rate, url: @form_url, html: { id: @form_id } do |f| %> + <%= page_with_sidebar do %> + <%= page_with_sidebar_main do %> + <%= render component("ui/panel").new do %> + <%= f.text_field(:name) %> + <%= f.select(:zone_id, zone_options) %> + <%= f.select(:tax_category_ids, tax_category_options, multiple: true) %> + <%= f.switch_field(:show_rate_in_label, hint: t(".hints.show_rate_in_label")) %> + <% end %> + + <%= render component("ui/panel").new(title: t(".calculation")) do %> + <%= f.select(:calculator_type, calculator_options) %> + <%= f.text_field(:amount, type: :number, step: 0.01) %> + <%= f.select(:level, level_options) %> + <%= f.switch_field(:included_in_price, hint: t(".hints.included_in_price")) %> + <% end %> + <% end %> + <%= page_with_sidebar_aside do %> + <%= render component("ui/panel").new(title: t(".validity")) do %> + <%= f.text_field(:starts_at, type: :date) %> + <%= f.text_field(:expires_at, type: :date) %> + <% end %> + <% end %> + <% end %> +<% end %> diff --git a/admin/app/components/solidus_admin/tax_rates/form/component.rb b/admin/app/components/solidus_admin/tax_rates/form/component.rb new file mode 100644 index 00000000000..bf22564cf88 --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/form/component.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class SolidusAdmin::TaxRates::Form::Component < SolidusAdmin::BaseComponent + include SolidusAdmin::Layout::PageHelpers + + def initialize(tax_rate:, form_url:, form_id:) + @tax_rate = tax_rate + @form_url = form_url + @form_id = form_id + end + + private + + def zone_options + @zone_options ||= Spree::Zone.order(:name).map { [_1.name, _1.id] } + end + + def tax_category_options + @tax_category_options ||= Spree::TaxCategory.order(:name).map { [_1.name, _1.id] } + end + + def calculator_options + @calculator_options ||= Rails.application.config.spree.calculators.tax_rates.map { [_1.description, _1.name] } + end + + def level_options + @level_options ||= Spree::TaxRate.levels.keys.map { [t(".levels.#{_1}"), _1] } + end +end diff --git a/admin/app/components/solidus_admin/tax_rates/form/component.yml b/admin/app/components/solidus_admin/tax_rates/form/component.yml new file mode 100644 index 00000000000..81cc1df652c --- /dev/null +++ b/admin/app/components/solidus_admin/tax_rates/form/component.yml @@ -0,0 +1,9 @@ +en: + calculation: "Calculation" + hints: + included_in_price: "Enables automatic inclusion of the applicable tax amount within product prices, providing customers with an all-inclusive pricing experience." + show_rate_in_label: "Allows users to display the applicable tax rate alongside product prices for transparent and informative pricing." + levels: + item: "Item level" + order: "Order level" + validity: "Validity" diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index ad8164abb08..29ca4c06853 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -394,12 +394,15 @@ en: tax_code: Tax Code spree/tax_rate: amount: Rate + calculator_type: Calculator expires_at: Expiration Date included_in_price: Included in Price + level: Tax Rate Level name: Name show_rate_in_label: Show Rate in Label starts_at: Start Date tax_categories: Tax Categories + tax_category_ids: Tax Categories spree/taxon: description: Description icon: Icon From 9b0b1cf68745f8c35397a7ae03dd811bf5d069de Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Fri, 27 Jun 2025 18:06:48 +0200 Subject: [PATCH 12/16] Add "#switch" feature helper --- admin/lib/solidus_admin/testing_support/feature_helpers.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/admin/lib/solidus_admin/testing_support/feature_helpers.rb b/admin/lib/solidus_admin/testing_support/feature_helpers.rb index 6f28ad553cb..cd81a9f46c0 100644 --- a/admin/lib/solidus_admin/testing_support/feature_helpers.rb +++ b/admin/lib/solidus_admin/testing_support/feature_helpers.rb @@ -61,6 +61,11 @@ def clear_search find('button[aria-label="Clear"]').click end end + + def switch(locator, on: true) + checkbox = find(:label, text: locator).find(:checkbox) + on ? checkbox.check : checkbox.uncheck + end end end end From 2de96009e9269397b0437306ea45f76ccca4db31 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 19:55:46 +0200 Subject: [PATCH 13/16] Render form on new rate page --- .../tax_rates/new/component.html.erb | 2 + admin/config/locales/tax_rates.en.yml | 2 + admin/spec/features/tax_rates_spec.rb | 49 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/admin/app/components/solidus_admin/tax_rates/new/component.html.erb b/admin/app/components/solidus_admin/tax_rates/new/component.html.erb index 53c048bd4c4..c2dab459474 100644 --- a/admin/app/components/solidus_admin/tax_rates/new/component.html.erb +++ b/admin/app/components/solidus_admin/tax_rates/new/component.html.erb @@ -14,4 +14,6 @@ <%= render component("ui/button").new(tag: :button, text: t(".save"), form: form_id) %> <% end %> <% end %> + + <%= render component("tax_rates/form").new(tax_rate: @tax_rate, form_url:, form_id:) %> <% end %> diff --git a/admin/config/locales/tax_rates.en.yml b/admin/config/locales/tax_rates.en.yml index b95ee13ed99..36572ee0cd7 100644 --- a/admin/config/locales/tax_rates.en.yml +++ b/admin/config/locales/tax_rates.en.yml @@ -2,5 +2,7 @@ en: solidus_admin: tax_rates: title: "Tax Rates" + create: + success: "Tax rate was successfully created." destroy: success: "Tax rates were successfully removed." diff --git a/admin/spec/features/tax_rates_spec.rb b/admin/spec/features/tax_rates_spec.rb index c0e1809c7a9..b38386aa850 100644 --- a/admin/spec/features/tax_rates_spec.rb +++ b/admin/spec/features/tax_rates_spec.rb @@ -21,4 +21,53 @@ expect(Spree::TaxRate.count).to eq(1) expect(page).to be_axe_clean end + + context "creating new tax rate" do + before do + create(:zone, name: "EU") + create(:tax_category, name: "Default") + create(:tax_category, name: "Specific") + end + + it "creates new tax rate" do + visit "/admin/tax_rates" + click_on "Add new" + expect(page).to have_current_path("/admin/tax_rates/new") + expect(page).to be_axe_clean + + fill_in "Name", with: "Clothing" + solidus_select "EU", from: "Zone" + solidus_select %w[Default Specific], from: "Tax Categories" + switch "Show Rate in Label" + solidus_select "Default Tax", from: "Calculator" + fill_in "Rate", with: "0.18" + solidus_select "Item level", from: "Tax Rate Level" + switch "Included in Price" + fill_in "Start Date", with: 1.month.ago + fill_in "Expiration Date", with: 1.year.from_now + within("header") { click_on "Save" } + + expect(page).to have_current_path("/admin/tax_rates") + expect(page).to have_content("Tax rate was successfully created.") + expect(page).to have_content("EU") + expect(page).to have_content("Clothing") + expect(page).to have_content("Default, Specific") + expect(page).to have_content("18.0%") + expect(page).to have_content(1.year.from_now.to_date.to_s) + expect(page).to have_content("Default Tax") + end + + context "with invalid attributes" do + it "shows validation errors" do + visit "/admin/tax_rates" + click_on "Add new" + + within("header") { click_on "Save" } + + expect(page).to have_current_path("/admin/tax_rates/new") + expect(page).to have_content("can't be blank") + expect(page).to have_content("is not a number") + end + end + end end From 734a9a66fdf6e1c8749dbeb0d24b1fbdfb8e6a5b Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 22:44:38 +0200 Subject: [PATCH 14/16] Render form on edit rate page --- .../tax_rates/edit/component.html.erb | 2 + admin/config/locales/tax_rates.en.yml | 2 + admin/spec/features/tax_rates_spec.rb | 44 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/admin/app/components/solidus_admin/tax_rates/edit/component.html.erb b/admin/app/components/solidus_admin/tax_rates/edit/component.html.erb index 53c048bd4c4..c2dab459474 100644 --- a/admin/app/components/solidus_admin/tax_rates/edit/component.html.erb +++ b/admin/app/components/solidus_admin/tax_rates/edit/component.html.erb @@ -14,4 +14,6 @@ <%= render component("ui/button").new(tag: :button, text: t(".save"), form: form_id) %> <% end %> <% end %> + + <%= render component("tax_rates/form").new(tax_rate: @tax_rate, form_url:, form_id:) %> <% end %> diff --git a/admin/config/locales/tax_rates.en.yml b/admin/config/locales/tax_rates.en.yml index 36572ee0cd7..668bd036fd6 100644 --- a/admin/config/locales/tax_rates.en.yml +++ b/admin/config/locales/tax_rates.en.yml @@ -6,3 +6,5 @@ en: success: "Tax rate was successfully created." destroy: success: "Tax rates were successfully removed." + update: + success: "Tax rate was successfully updated." diff --git a/admin/spec/features/tax_rates_spec.rb b/admin/spec/features/tax_rates_spec.rb index b38386aa850..a5cb4b8cba6 100644 --- a/admin/spec/features/tax_rates_spec.rb +++ b/admin/spec/features/tax_rates_spec.rb @@ -70,4 +70,48 @@ end end end + + context "updating tax rate" do + before do + create(:tax_rate, + name: "Clothing", + zone: create(:zone, name: "US"), + tax_categories: [create(:tax_category, name: "Default")], + amount: 0.3) + create(:zone, name: "EU") + create(:tax_category, name: "Specific") + end + + it "updates tax rate" do + visit "/admin/tax_rates" + click_on "Clothing" + + fill_in "Name", with: "Food" + solidus_select "EU", from: "Zone" + solidus_select "Specific", from: "Tax Categories" + fill_in "Rate", with: "0.18" + + within("header") { click_on "Save" } + + expect(page).to have_content("Tax rate was successfully updated.") + expect(page).to have_content("EU") + expect(page).to have_content("Food") + expect(page).to have_content("Default, Specific") + expect(page).to have_content("18.0%") + end + + context "with invalid attributes" do + it "shows validation errors" do + visit "/admin/tax_rates" + click_on "Clothing" + + fill_in "Rate", with: "" + + within("header") { click_on "Save" } + + expect(page).to have_content("can't be blank") + expect(page).to have_content("is not a number") + end + end + end end From 533a8759e42d6365e785360f1f8c805c605839a1 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Thu, 26 Jun 2025 23:07:42 +0200 Subject: [PATCH 15/16] Avoid N+1 when loading tax rates index --- admin/app/controllers/solidus_admin/tax_rates_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/admin/app/controllers/solidus_admin/tax_rates_controller.rb b/admin/app/controllers/solidus_admin/tax_rates_controller.rb index ae36cf09edc..47821975890 100644 --- a/admin/app/controllers/solidus_admin/tax_rates_controller.rb +++ b/admin/app/controllers/solidus_admin/tax_rates_controller.rb @@ -6,6 +6,10 @@ class TaxRatesController < SolidusAdmin::ResourcesController def resource_class = Spree::TaxRate + def resources_collection + resource_class.includes(:zone, :tax_categories, :calculator) + end + def resources_sorting_options = { created_at: :desc, id: :desc } def permitted_resource_params From d485f9add19ab31bc93d7926240571071fb8c713 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Fri, 27 Jun 2025 18:33:28 +0200 Subject: [PATCH 16/16] Add request specs --- admin/spec/requests/solidus_admin/tax_rates_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 admin/spec/requests/solidus_admin/tax_rates_spec.rb diff --git a/admin/spec/requests/solidus_admin/tax_rates_spec.rb b/admin/spec/requests/solidus_admin/tax_rates_spec.rb new file mode 100644 index 00000000000..9bc7288160b --- /dev/null +++ b/admin/spec/requests/solidus_admin/tax_rates_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "spec_helper" +require 'solidus_admin/testing_support/shared_examples/crud_resource_requests' + +RSpec.describe "SolidusAdmin::TaxRatesController", type: :request do + include_examples 'CRUD resource requests', 'tax_rate' do + let(:resource_class) { Spree::TaxRate } + let(:valid_attributes) { { amount: 1, calculator_type: "Spree::Calculator::DefaultTax" } } + let(:invalid_attributes) { { amount: "", calculator_type: nil } } + end +end