Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,49 @@
) %>
<% end %>

<%= render component("ui/panel").new(title: t(".options")) do %>
<%= render component("ui/forms/field").select(
f,
:option_type_ids,
option_type_options,
multiple: true,
"size" => option_type_options.size,
<%= render component("ui/panel").new(title: t(".options")) do |panel| %>
<% if @product.product_option_types.present? %>
<% panel.with_section do %>
<div class="flex flex-col gap-4" data-controller="sortable" data-sortable-handle-value=".handle">
<% @product.product_option_types.includes(option_type: :option_values).order(:position).each do |product_option| %>
<div class="flex gap-2 justify-between" data-sortable-url=<%= solidus_admin.move_product_option_type_path(product_option) %>>
<div class="flex gap-2">
<div class="flex items-center">
<%= render component("ui/icon").new(name: "draggable", class: "w-6 h-6 cursor-grab handle fill-gray-500") %>
</div>
<div class="flex flex-col gap-2">
<span class="font-semibold text-sm"><%= product_option.name %>:<%= product_option.presentation %></span>
<div class="flex gap-2 flex-wrap">
<% product_option.option_values.each do |value| %>
<%= render component("ui/badge").new(name: "#{value.name}:#{value.presentation}") %>
<% end %>
</div>
</div>
</div>
<div class="flex items-center">
<%= render component("ui/button").new(tag: :a, href: spree.edit_admin_option_type_path(product_option.option_type), scheme: :secondary, text: t(".edit")) %>
</div>
</div>
<% end %>
</div>
<% end %>
<% end %>

<div class="flex gap-4 justify-between items-end">
<%= hidden_field_tag "#{f.object_name}[option_type_ids][]", nil %>
<%= render component("ui/forms/field").select(
f,
:option_type_ids,
option_type_options,
multiple: true,
"size" => option_type_options.size,
) %>
<%= render component("ui/button").new(type: :submit, text: t(".save")) %>
</div>

<% panel.with_action(
name: t(".manage_options"),
href: solidus_admin.option_types_path
) %>
<% end %>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
20 changes: 11 additions & 9 deletions admin/app/components/solidus_admin/products/show/component.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
en:
save: "Save"
back: "Back"
duplicate: "Duplicate"
view: "View online"
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.<br> Empty date indicates no availability."
discontinue_on_html: "Product availability ends from the set date.<br> 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"
Expand All @@ -14,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.<br> Empty date indicates no availability."
discontinue_on_html: "Product availability ends from the set date.<br> 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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

class SolidusAdmin::ProductOptionTypesController < SolidusAdmin::BaseController
include SolidusAdmin::Moveable
end
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ def update
@product = Spree::Product.friendly.find(params[:id])

if @product.update(product_params)
flash[:success] = 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(", ")
Expand Down
2 changes: 2 additions & 0 deletions admin/config/locales/products.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ en:
success: "Products were successfully discontinued."
activate:
success: "Products were successfully activated."
update:
success: "Product was successfully updated."
1 change: 1 addition & 0 deletions admin/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions admin/lib/solidus_admin/testing_support/feature_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>] which option(s) to select
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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")
Expand Down
78 changes: 78 additions & 0 deletions admin/spec/features/product_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
11 changes: 11 additions & 0 deletions admin/spec/requests/solidus_admin/product_option_types_spec.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion admin/spec/requests/solidus_admin/products_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions core/app/models/spree/product_option_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ class ProductOptionType < Spree::Base
belongs_to :product, class_name: 'Spree::Product', inverse_of: :product_option_types, touch: true
belongs_to :option_type, class_name: 'Spree::OptionType', inverse_of: :product_option_types
acts_as_list scope: :product

delegate :name, :presentation, :option_values, to: :option_type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This delegation seems somewhat arbitrary. I don't think we should add to the API of this class just to make a couple of calls in the view more convenient. I think this actually makes the view more confusing because it obfuscates where this information is.

end
end
1 change: 1 addition & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down