diff --git a/admin/app/components/solidus_admin/products/show/component.html.erb b/admin/app/components/solidus_admin/products/show/component.html.erb index 36bb5bef790..d30146e7e5c 100644 --- a/admin/app/components/solidus_admin/products/show/component.html.erb +++ b/admin/app/components/solidus_admin/products/show/component.html.erb @@ -76,8 +76,44 @@ ) %> <% end %> - <%= render component("ui/panel").new(title: t(".options")) do %> - <%= f.select(:option_type_ids, option_type_options, multiple: true) %> + <%= render component("ui/panel").new(title: t(".options")) do |panel| %> + <% if @product.product_option_types.present? %> + <% panel.with_section do %> +
+ <% @product.product_option_types.includes(option_type: :option_values).order(:position).each do |product_option| %> +
> +
+
+ <%= render component("ui/icon").new(name: "draggable", class: "w-6 h-6 cursor-grab handle fill-gray-500") %> +
+
+ <%= product_option.option_type.name %>:<%= product_option.option_type.presentation %> +
+ <% product_option.option_type.option_values.each do |value| %> + <%= render component("ui/badge").new(name: "#{value.name}:#{value.presentation}") %> + <% end %> +
+
+
+
+ <%= render component("ui/button").new(tag: :a, href: spree.edit_admin_option_type_path(product_option.option_type), scheme: :secondary, text: t(".edit")) %> +
+
+ <% end %> +
+ <% end %> + <% end %> + +
+ <%= hidden_field_tag "#{f.object_name}[option_type_ids][]", nil %> + <%= f.select(:option_type_ids, option_type_options, multiple: true) %> + <%= render component("ui/button").new(type: :submit, text: t(".save")) %> +
+ + <% panel.with_action( + name: t(".manage_options"), + href: solidus_admin.option_types_path + ) %> <% end %> <%= render component("ui/panel").new(title: t(".specifications")) do |panel| %> diff --git a/admin/app/components/solidus_admin/products/show/component.rb b/admin/app/components/solidus_admin/products/show/component.rb index 66ae89250fd..9bef1292513 100644 --- a/admin/app/components/solidus_admin/products/show/component.rb +++ b/admin/app/components/solidus_admin/products/show/component.rb @@ -22,7 +22,7 @@ def taxon_options def option_type_options @option_type_options ||= Spree::OptionType.order(:presentation).pluck(:presentation, :name, :id).map do - ["#{_1} (#{_2})", _3] + ["#{_2}:#{_1}", _3] end end diff --git a/admin/app/components/solidus_admin/products/show/component.yml b/admin/app/components/solidus_admin/products/show/component.yml index 6d6d7cc1b9d..9d9e3855b6f 100644 --- a/admin/app/components/solidus_admin/products/show/component.yml +++ b/admin/app/components/solidus_admin/products/show/component.yml @@ -1,9 +1,17 @@ en: - duplicate: "Duplicate" - view: "View online" + back: "Back" delete: "Delete" delete_confirmation: "Are you sure you want to delete this product?" + duplicate: "Duplicate" + edit: "Edit" + hints: + available_on_html: "Product availability starts from the set date.
Empty date indicates no availability." + discontinue_on_html: "Product availability ends from the set date.
Empty date indicates continuous availability." + promotionable_html: "Promotions can apply to this product" + shipping_category_html: "Manage Shipping in Settings" + tax_category_html: "Manage Taxes in Settings" manage_images: "Manage images" + manage_options: "Manage option types" manage_properties: "Manage product specifications" manage_stock: "Manage stock" media: "Media" @@ -12,13 +20,9 @@ en: pricing: "Pricing" product_organization: "Product organization" publishing: "Publishing" + save: "Save" seo: "SEO" stock: "Stock" shipping: "Shipping" specifications: "Specifications" - hints: - available_on_html: "Product availability starts from the set date.
Empty date indicates no availability." - discontinue_on_html: "Product availability ends from the set date.
Empty date indicates continuous availability." - promotionable_html: "Promotions can apply to this product" - shipping_category_html: "Manage Shipping in Settings" - tax_category_html: "Manage Taxes in Settings" + view: "View online" diff --git a/admin/app/controllers/solidus_admin/product_option_types_controller.rb b/admin/app/controllers/solidus_admin/product_option_types_controller.rb new file mode 100644 index 00000000000..640ef324d5b --- /dev/null +++ b/admin/app/controllers/solidus_admin/product_option_types_controller.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class SolidusAdmin::ProductOptionTypesController < SolidusAdmin::BaseController + include SolidusAdmin::Moveable +end diff --git a/admin/app/controllers/solidus_admin/products_controller.rb b/admin/app/controllers/solidus_admin/products_controller.rb index 4475d11cb47..bb5822813f8 100644 --- a/admin/app/controllers/solidus_admin/products_controller.rb +++ b/admin/app/controllers/solidus_admin/products_controller.rb @@ -43,11 +43,7 @@ def update @product = Spree::Product.friendly.find(params[:id]) if @product.update(product_params) - flash[:notice] = t('spree.successfully_updated', resource: [ - Spree::Product.model_name.human, - @product.name.inspect, - ].join(' ')) - + flash[:success] = t('.success') redirect_to action: :show, status: :see_other else flash.now[:error] = @product.errors.full_messages.join(", ") diff --git a/admin/config/locales/products.en.yml b/admin/config/locales/products.en.yml index fb075351261..c69e3991b14 100644 --- a/admin/config/locales/products.en.yml +++ b/admin/config/locales/products.en.yml @@ -8,3 +8,5 @@ en: success: "Products were successfully discontinued." activate: success: "Products were successfully activated." + update: + success: "Product was successfully updated." diff --git a/admin/config/routes.rb b/admin/config/routes.rb index 57306812340..ffb8e94999a 100644 --- a/admin/config/routes.rb +++ b/admin/config/routes.rb @@ -87,4 +87,5 @@ admin_resources :roles, except: [:show] admin_resources :adjustment_reasons, except: [:show] admin_resources :store_credit_reasons, except: [:show] + admin_resources :product_option_types, only: [], sortable: true end diff --git a/admin/lib/solidus_admin/testing_support/feature_helpers.rb b/admin/lib/solidus_admin/testing_support/feature_helpers.rb index bf47f18d52c..f9828217100 100644 --- a/admin/lib/solidus_admin/testing_support/feature_helpers.rb +++ b/admin/lib/solidus_admin/testing_support/feature_helpers.rb @@ -33,6 +33,10 @@ def select_row(text) end end + def panel(title:) + find("section", text: title).find(:xpath, "..") + end + # Select options from a "solidus-select" field # # @param value [String, Array] which option(s) to select @@ -52,6 +56,14 @@ def solidus_select(value, from:) end end + def solidus_unselect(value, from:) + input = find_field(from, visible: :all) + Array.wrap(value).each do |val| + item = input.sibling("div", text: val) + item.find("a").click + end + end + def checkbox(locator) find(:checkbox, locator) end diff --git a/admin/lib/solidus_admin/testing_support/shared_examples/moveable.rb b/admin/lib/solidus_admin/testing_support/shared_examples/moveable.rb index a321ea5faa4..53fc48558c0 100644 --- a/admin/lib/solidus_admin/testing_support/shared_examples/moveable.rb +++ b/admin/lib/solidus_admin/testing_support/shared_examples/moveable.rb @@ -22,6 +22,7 @@ RSpec.shared_examples_for "features: sortable" do let(:factory_attrs) { {} } let(:scope) { "body" } + let(:handle) { nil } before do create(factory, displayed_attribute => "First", position: 1, **factory_attrs) @@ -35,7 +36,10 @@ expect(find("[data-controller='sortable']").all(:xpath, "./*").last).to have_text("Second") rows = find("[data-controller='sortable']").all(:xpath, "./*") - rows[1].drag_to rows[0] + target = rows[0] + source = rows[1] + source = source.find(handle) if handle + source.drag_to target expect(find("[data-controller='sortable']").all(:xpath, "./*").first).to have_text("Second") expect(find("[data-controller='sortable']").all(:xpath, "./*").last).to have_text("First") diff --git a/admin/spec/features/product_spec.rb b/admin/spec/features/product_spec.rb index 396ab30a2cf..0a2e81ad4db 100644 --- a/admin/spec/features/product_spec.rb +++ b/admin/spec/features/product_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'spec_helper' +require "solidus_admin/testing_support/shared_examples/moveable" describe "Product", type: :feature do before do @@ -52,4 +53,81 @@ expect(page).to have_content("Name can't be blank") expect(page).to be_axe_clean end + + describe "option types", :js do + before do + create(:option_type, name: "clothing-size", presentation: "Size").tap do |option_type| + option_type.option_values << [ + create(:option_value, name: "S", presentation: "Small"), + create(:option_value, name: "M", presentation: "Medium") + ] + end + + create(:option_type, name: "clothing-color", presentation: "Color").tap do |option_type| + option_type.option_values << [ + create(:option_value, name: "brown", presentation: "Brown"), + create(:option_value, name: "red", presentation: "Red") + ] + end + end + + let!(:product) { create(:product, name: "Just a product", slug: 'just-a-prod', price: 19.99) } + + it "updates option types" do + visit "/admin/products/just-a-prod" + solidus_select(%w[clothing-size:Size clothing-color:Color], from: "Option Types") + options_panel = panel(title: "Options") + # for some reason capybara on circle ci does not register a form submit when clicking "Save" within options panel, + # so we have to resort to Save button in the header + within("header") { click_on "Save" } + + expect(options_panel).to have_content("clothing-size:Size") + expect(options_panel).to have_content("S:Small") + expect(options_panel).to have_content("M:Medium") + expect(options_panel).to have_content("clothing-color:Color") + expect(options_panel).to have_content("brown:Brown") + expect(options_panel).to have_content("red:Red") + + solidus_unselect(%w[clothing-size:Size clothing-color:Color], from: "Option Types") + within(options_panel) { click_on "Save" } + + expect(options_panel).not_to have_content("clothing-size:Size") + expect(options_panel).not_to have_content("S:Small") + expect(options_panel).not_to have_content("M:Medium") + expect(options_panel).not_to have_content("clothing-color:Color") + expect(options_panel).not_to have_content("brown:Brown") + expect(options_panel).not_to have_content("red:Red") + end + + context "clicking on Edit" do + # skipping test until updated option types UI is merged + # https://github.com/solidusio/solidus/pull/6236 + xit "leads to option type edit page" do + option_type = create(:option_type) + product.option_types << option_type + visit "/admin/products/just-a-prod" + + within(panel(title: "Options")) { click_on "Edit" } + expect(page).to have_current_path("/admin/option_types/#{option_type.id}/edit") + end + end + + context "clicking on Manage option types" do + it "leads to option types index page" do + visit "/admin/products/just-a-prod" + + within(panel(title: "Options")) { click_on "Manage option types" } + expect(page).to have_current_path("/admin/option_types") + end + end + + it_behaves_like "features: sortable" do + let(:product) { create(:product) } + let(:factory) { :option_type } + let(:factory_attrs) { { products: [product] } } + let(:displayed_attribute) { :name } + let(:handle) { ".handle" } + let(:path) { solidus_admin.product_path(product) } + end + end end diff --git a/admin/spec/requests/solidus_admin/product_option_types_spec.rb b/admin/spec/requests/solidus_admin/product_option_types_spec.rb new file mode 100644 index 00000000000..efbe1c8d338 --- /dev/null +++ b/admin/spec/requests/solidus_admin/product_option_types_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require "spec_helper" +require "solidus_admin/testing_support/shared_examples/moveable" + +RSpec.describe "SolidusAdmin::ProductOptionTypesController", type: :request do + it_behaves_like "requests: moveable" do + let(:factory) { :product_option_type } + let(:request_path) { solidus_admin.move_product_option_type_path(record, format: :js) } + end +end diff --git a/admin/spec/requests/solidus_admin/products_spec.rb b/admin/spec/requests/solidus_admin/products_spec.rb index 0addacf2c72..b2d4c1fc795 100644 --- a/admin/spec/requests/solidus_admin/products_spec.rb +++ b/admin/spec/requests/solidus_admin/products_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe "SolidusAdmin::PropertiesController", type: :request do +RSpec.describe "SolidusAdmin::ProductsController", type: :request do let(:admin_user) { create(:admin_user) } before do diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index ad8164abb08..1d259f176be 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -182,6 +182,7 @@ en: meta_title: Meta Title name: Name on_hand: On Hand + option_type_ids: Option Types price: Master Price primary_taxon: Primary Taxon primary_taxon_id: Primary Taxon