From de65fd6658868e2b860c5806b42cd978a32c9302 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Fri, 30 May 2025 19:58:08 +0200 Subject: [PATCH 1/3] Add missing spacing in UI panel section According to designs each section in panel is expected to have 24px of column-gap. --- admin/app/components/solidus_admin/ui/panel/component.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/app/components/solidus_admin/ui/panel/component.rb b/admin/app/components/solidus_admin/ui/panel/component.rb index e721ce10e25..6f86f8509fc 100644 --- a/admin/app/components/solidus_admin/ui/panel/component.rb +++ b/admin/app/components/solidus_admin/ui/panel/component.rb @@ -32,6 +32,7 @@ def initialize(title: nil, title_hint: nil) def render_section(wide: false, high: false, **args, &block) tag.section(**args, class: " border-gray-100 border-t w-full first-of-type:border-t-0 + flex flex-col gap-6 #{'px-6' unless wide} #{'py-4' unless high} #{args[:class]} From c1225e19effdf946d3683d20e9c856b374b99c98 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Fri, 30 May 2025 22:48:58 +0200 Subject: [PATCH 2/3] Cleanup product form Translate strings and reorganize translation keys a bit. --- .../products/show/component.html.erb | 33 +++++++++---------- .../solidus_admin/products/show/component.yml | 23 +++++++++---- 2 files changed, 31 insertions(+), 25 deletions(-) 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 56ee060d605..6504c2661ec 100644 --- a/admin/app/components/solidus_admin/products/show/component.html.erb +++ b/admin/app/components/solidus_admin/products/show/component.html.erb @@ -26,13 +26,13 @@ <%= form_for @product, url: solidus_admin.product_path(@product), html: { id: form_id } do |f| %> <%= page_with_sidebar do %> <%= page_with_sidebar_main do %> - <%= render component('ui/panel').new do %> + <%= render component("ui/panel").new do %> <%= render component("ui/forms/field").text_field(f, :name) %> <%= render component("ui/forms/field").text_field(f, :slug) %> <%= render component("ui/forms/field").text_area(f, :description) %> <% end %> - <%= render component('ui/panel').new(title: 'SEO') do %> + <%= render component("ui/panel").new(title: t(".seo")) do %> <%= render component("ui/forms/field").text_field(f, :meta_title) %> <%= render component("ui/forms/field").text_field(f, :meta_description) %> <%= render component("ui/forms/field").text_area(f, :meta_keywords) %> @@ -41,17 +41,17 @@ f, :condition, condition_options, - include_blank: t('spree.unset'), + include_blank: t("spree.unset"), ) %> <% end %> - <%= render component('ui/panel').new(title: "Media") do |panel| %> + <%= render component("ui/panel").new(title: t(".media")) do |panel| %> <% panel.with_action( name: t(".manage_images"), href: spree.admin_product_images_path(@product) ) %> <% end %> - <%= render component('ui/panel').new(title: 'Pricing') do %> + <%= render component("ui/panel").new(title: t(".pricing")) do %> <%= render component("ui/forms/field").text_field(f, :price) %>
<%= render component("ui/forms/field").text_field(f, :cost_price) %> @@ -59,7 +59,7 @@
<% end %> - <%= render component('ui/panel').new(title: 'Stock') do |panel| %> + <%= render component("ui/panel").new(title: t(".stock")) do |panel| %> <%= render component("ui/forms/field").text_field(f, :sku) %> <% panel.with_action( @@ -68,25 +68,22 @@ ) %> <% end %> - <%= render component('ui/panel').new(title: 'Shipping') do %> + <%= render component("ui/panel").new(title: t(".shipping")) do %> <%= render component("ui/forms/field").select( f, :shipping_category_id, [[t(".none"), nil]] + Spree::ShippingCategory.order(:name).pluck(:name, :id), - tip: t(".hints.shipping_category_html"), + tip: t(".hints.shipping_category_html") ) %> <%= render component("ui/forms/field").select( f, :tax_category_id, [[t(".none"), nil]] + Spree::TaxCategory.order(:name).pluck(:name, :id), - tip: t( - ".hints.tax_category_html", - default_tax_category: Spree::TaxCategory.default&.name - ), + tip: t(".hints.tax_category_html") ) %> <% end %> - <%= render component('ui/panel').new(title: "Options") do %> + <%= render component("ui/panel").new(title: t(".options")) do %> <%= render component("ui/forms/field").select( f, :option_type_ids, @@ -96,7 +93,7 @@ ) %> <% end %> - <%= render component('ui/panel').new(title: "Specifications") do |panel| %> + <%= render component("ui/panel").new(title: t(".specifications")) do |panel| %> <% panel.with_action( name: t(".manage_properties"), href: spree.admin_product_product_properties_path(@product) @@ -105,18 +102,18 @@ <% end %> <%= page_with_sidebar_aside do %> - <%= render component('ui/panel').new(title: "Publishing") do %> + <%= render component("ui/panel").new(title: t(".publishing")) do %> <%= render component("ui/forms/field").text_field( f, :available_on, - hint: t(".available_on_html"), + hint: t(".hints.available_on_html"), type: :date, value: f.object.available_on&.to_date ) %> <%= render component("ui/forms/field").text_field( f, :discontinue_on, - hint: t(".discontinue_on_html"), + hint: t(".hints.discontinue_on_html"), type: :date, value: f.object.discontinue_on&.to_date ) %> @@ -130,7 +127,7 @@ <% end %> - <%= render component('ui/panel').new(title: "Product organization") do %> + <%= render component("ui/panel").new(title: t(".product_organization")) do %> <%= render component("ui/forms/field").select( f, :taxon_ids, diff --git a/admin/app/components/solidus_admin/products/show/component.yml b/admin/app/components/solidus_admin/products/show/component.yml index 4b680f81c2f..58a100b8332 100644 --- a/admin/app/components/solidus_admin/products/show/component.yml +++ b/admin/app/components/solidus_admin/products/show/component.yml @@ -4,14 +4,23 @@ en: duplicate: "Duplicate" view: "View online" delete: "Delete" - none: "None" + delete_confirmation: "Are you sure you want to delete this product?" manage_images: "Manage images" manage_properties: "Manage product specifications" manage_stock: "Manage stock" - delete_confirmation: "Are you sure you want to delete this product?" - 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.' + media: "Media" + none: "None" + options: "Options" + pricing: "Pricing" + product_organization: "Product organization" + publishing: "Publishing" + seo: "SEO" + stock: "Stock" + shipping: "Shipping" + specifications: "Specifications" hints: - promotionable_html: Promotions can apply to this product - shipping_category_html: Manage Shipping in Settings - tax_category_html: Manage Taxes in Settings + 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" From b4b15bb2c206d79ce5654021e96c87bf01320f69 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Mon, 2 Jun 2025 17:45:46 +0200 Subject: [PATCH 3/3] Update permitted params for products Use explicit product params to permit. Removes #split_params as not needed because multiple select handles sending multiple values correctly. --- .../solidus_admin/products_controller.rb | 17 +++---- .../requests/solidus_admin/products_spec.rb | 47 +++++++++++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 admin/spec/requests/solidus_admin/products_spec.rb diff --git a/admin/app/controllers/solidus_admin/products_controller.rb b/admin/app/controllers/solidus_admin/products_controller.rb index 094c2c6ab08..e5f22336ed3 100644 --- a/admin/app/controllers/solidus_admin/products_controller.rb +++ b/admin/app/controllers/solidus_admin/products_controller.rb @@ -11,8 +11,6 @@ class ProductsController < SolidusAdmin::BaseController search_scope(:in_stock) { _1.where(id: Spree::Variant.in_stock.distinct.select(:product_id)) } search_scope(:out_of_stock) { _1.where.not(id: Spree::Variant.in_stock.distinct.select(:product_id)) } - before_action :split_params, only: [:update] - def index products = apply_search_to( Spree::Product.includes(:master, :variants), @@ -44,7 +42,7 @@ def show def update @product = Spree::Product.friendly.find(params[:id]) - if @product.update(params.require(:product).permit!) + if @product.update(product_params) flash[:success] = t('spree.successfully_updated', resource: [ Spree::Product.model_name.human, @product.name.inspect, @@ -101,13 +99,12 @@ def activate redirect_to products_path, status: :see_other end - def split_params - if params[:product][:taxon_ids].present? - params[:product][:taxon_ids] = params[:product][:taxon_ids].split(',') - end - if params[:product][:option_type_ids].present? - params[:product][:option_type_ids] = params[:product][:option_type_ids].split(',') - end + private + + def product_params + params.require(:product).permit(:name, :slug, :description, :meta_title, :meta_description, :meta_keywords, :gtin, + :condition, :price, :cost_price, :cost_currency, :sku, :shipping_category_id, :tax_category_id, + :available_on, :discontinue_on, :promotionable, option_type_ids: [], taxon_ids: []) end end end diff --git a/admin/spec/requests/solidus_admin/products_spec.rb b/admin/spec/requests/solidus_admin/products_spec.rb new file mode 100644 index 00000000000..0addacf2c72 --- /dev/null +++ b/admin/spec/requests/solidus_admin/products_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "SolidusAdmin::PropertiesController", type: :request do + let(:admin_user) { create(:admin_user) } + + before do + allow_any_instance_of(SolidusAdmin::BaseController).to receive(:spree_current_user).and_return(admin_user) + end + + describe "PATCH #update" do + let(:product) { create(:product) } + let(:params) do + { + name: "T-Shirt", + description: "Nice T-Shirt", + slug: "nice-t-shirt", + meta_title: "Nice T-Shirt", + meta_description: "It is a really nice T-Shirt", + meta_keywords: "tshirt, tee", + gtin: "12345", + condition: "new", + price: 100, + cost_price: 100, + cost_currency: "USD", + sku: "T123", + shipping_category_id: create(:shipping_category).id, + tax_category_id: create(:tax_category).id, + available_on: "2025-05-28".to_date, + discontinue_on: "2026-01-06".to_date, + promotionable: true, + option_type_ids: [create(:option_type).id, create(:option_type).id], + taxon_ids: [create(:taxon).id, create(:taxon).id], + } + end + + it "updates product" do + patch solidus_admin.product_path(product), params: { product: params } + expect(response).to have_http_status(:see_other) + expect(product.reload).to have_attributes(params.except(Spree::Product::MASTER_ATTRIBUTES)) + %i[gtin condition price cost_price cost_currency sku].each do |attr| + expect(product.public_send(attr)).to eq(params[attr]) + end + end + end +end