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