spree_admin 5.3.5 → 5.4.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/controllers/concerns/spree/admin/order_concern.rb +1 -1
  4. data/app/controllers/spree/admin/action_text/video_embeds_controller.rb +1 -1
  5. data/app/controllers/spree/admin/addresses_controller.rb +1 -1
  6. data/app/controllers/spree/admin/admin_users_controller.rb +3 -3
  7. data/app/controllers/spree/admin/api_keys_controller.rb +56 -0
  8. data/app/controllers/spree/admin/assets_controller.rb +2 -2
  9. data/app/controllers/spree/admin/base_controller.rb +4 -4
  10. data/app/controllers/spree/admin/classifications_controller.rb +5 -4
  11. data/app/controllers/spree/admin/coupon_codes_controller.rb +1 -1
  12. data/app/controllers/spree/admin/customer_group_users_controller.rb +3 -2
  13. data/app/controllers/spree/admin/dashboard_controller.rb +2 -1
  14. data/app/controllers/spree/admin/digital_assets_controller.rb +1 -1
  15. data/app/controllers/spree/admin/exports_controller.rb +1 -2
  16. data/app/controllers/spree/admin/gift_cards_controller.rb +5 -5
  17. data/app/controllers/spree/admin/import_mappings_controller.rb +1 -1
  18. data/app/controllers/spree/admin/import_rows_controller.rb +1 -1
  19. data/app/controllers/spree/admin/integrations_controller.rb +1 -1
  20. data/app/controllers/spree/admin/invitations_controller.rb +6 -5
  21. data/app/controllers/spree/admin/line_items_controller.rb +1 -1
  22. data/app/controllers/spree/admin/markets_controller.rb +28 -0
  23. data/app/controllers/spree/admin/option_values_controller.rb +1 -1
  24. data/app/controllers/spree/admin/orders/adjustments_controller.rb +4 -4
  25. data/app/controllers/spree/admin/orders/billing_address_controller.rb +4 -4
  26. data/app/controllers/spree/admin/orders/customer_returns_controller.rb +1 -1
  27. data/app/controllers/spree/admin/orders/shipping_address_controller.rb +3 -3
  28. data/app/controllers/spree/admin/orders/user_controller.rb +4 -4
  29. data/app/controllers/spree/admin/orders_controller.rb +8 -4
  30. data/app/controllers/spree/admin/payments_controller.rb +3 -3
  31. data/app/controllers/spree/admin/price_list_products_controller.rb +1 -1
  32. data/app/controllers/spree/admin/price_rules_controller.rb +1 -1
  33. data/app/controllers/spree/admin/products_controller.rb +24 -15
  34. data/app/controllers/spree/admin/profile_controller.rb +1 -1
  35. data/app/controllers/spree/admin/promotion_actions_controller.rb +1 -1
  36. data/app/controllers/spree/admin/promotion_rules_controller.rb +1 -1
  37. data/app/controllers/spree/admin/promotions_controller.rb +1 -1
  38. data/app/controllers/spree/admin/refunds_controller.rb +1 -1
  39. data/app/controllers/spree/admin/reimbursements_controller.rb +2 -2
  40. data/app/controllers/spree/admin/resource_controller.rb +30 -11
  41. data/app/controllers/spree/admin/shipments_controller.rb +3 -3
  42. data/app/controllers/spree/admin/shipping_methods_controller.rb +1 -1
  43. data/app/controllers/spree/admin/store_credits_controller.rb +5 -5
  44. data/app/controllers/spree/admin/stores_controller.rb +1 -32
  45. data/app/controllers/spree/admin/taxons_controller.rb +3 -3
  46. data/app/controllers/spree/admin/translations_controller.rb +1 -0
  47. data/app/controllers/spree/admin/users_controller.rb +2 -2
  48. data/app/helpers/spree/admin/api_keys_helper.rb +32 -0
  49. data/app/helpers/spree/admin/base_helper.rb +6 -1
  50. data/app/helpers/spree/admin/json_preview_helper.rb +29 -25
  51. data/app/helpers/spree/admin/orders_filters_helper.rb +1 -1
  52. data/app/helpers/spree/admin/sortable_tree_helper.rb +1 -1
  53. data/app/helpers/spree/admin/stores_helper.rb +0 -4
  54. data/app/javascript/spree/admin/controllers/autocomplete_select_controller.js +5 -1
  55. data/app/javascript/spree/admin/controllers/search_clear_controller.js +1 -1
  56. data/app/javascript/spree/admin/controllers/select_controller.js +4 -0
  57. data/app/javascript/spree/admin/controllers/variants_form_controller.js +4 -3
  58. data/app/views/spree/admin/api_keys/_details.html.erb +51 -0
  59. data/app/views/spree/admin/api_keys/_form.html.erb +26 -0
  60. data/app/views/spree/admin/api_keys/_token_card.html.erb +28 -0
  61. data/app/views/spree/admin/api_keys/_usage_info.html.erb +16 -0
  62. data/app/views/spree/admin/api_keys/index.html.erb +9 -0
  63. data/app/views/spree/admin/api_keys/show.html.erb +26 -0
  64. data/app/views/spree/admin/classifications/_classification.html.erb +2 -2
  65. data/app/views/spree/admin/classifications/index.html.erb +1 -1
  66. data/app/views/spree/admin/classifications/new.html.erb +1 -1
  67. data/app/views/spree/admin/exports/create.turbo_stream.erb +1 -1
  68. data/app/views/spree/admin/exports/new.html.erb +3 -3
  69. data/app/views/spree/admin/json_previews/show.html.erb +6 -6
  70. data/app/views/spree/admin/markets/_form.html.erb +28 -0
  71. data/app/views/spree/admin/markets/edit.html.erb +1 -0
  72. data/app/views/spree/admin/markets/index.html.erb +9 -0
  73. data/app/views/spree/admin/markets/new.html.erb +1 -0
  74. data/app/views/spree/admin/orders/billing_address/_form.html.erb +2 -2
  75. data/app/views/spree/admin/orders/shipping_address/_form.html.erb +2 -2
  76. data/app/views/spree/admin/payment_methods/_form.html.erb +0 -12
  77. data/app/views/spree/admin/price_rules/forms/_market_rule.html.erb +7 -0
  78. data/app/views/spree/admin/products/_form.html.erb +0 -1
  79. data/app/views/spree/admin/products/form/_variants.html.erb +4 -3
  80. data/app/views/spree/admin/promotion_rules/forms/_country.html.erb +1 -1
  81. data/app/views/spree/admin/shared/_content_header.html.erb +1 -1
  82. data/app/views/spree/admin/shared/sidebar/_store_dropdown.html.erb +0 -33
  83. data/app/views/spree/admin/shared/sidebar/_store_nav.html.erb +2 -2
  84. data/app/views/spree/admin/shared/sortable_tree/_taxonomy.html.erb +2 -2
  85. data/app/views/spree/admin/stores/form/_basic.html.erb +10 -7
  86. data/app/views/spree/admin/stores/form/_checkout.html.erb +5 -8
  87. data/app/views/spree/admin/tables/columns/_api_key_status.html.erb +2 -0
  88. data/app/views/spree/admin/tables/columns/_api_key_type.html.erb +2 -0
  89. data/app/views/spree/admin/taxonomies/show.html.erb +1 -1
  90. data/app/views/spree/admin/taxons/_form.html.erb +2 -2
  91. data/app/views/spree/admin/taxons/edit.html.erb +1 -2
  92. data/app/views/spree/admin/taxons/update.turbo_stream.erb +1 -1
  93. data/app/views/spree/admin/users/_billing.html.erb +2 -2
  94. data/app/views/spree/admin/users/_shipping.html.erb +1 -1
  95. data/app/views/spree/admin/variants/_variant.html.erb +1 -1
  96. data/config/brakeman.ignore +28 -0
  97. data/config/initializers/spree_admin_navigation.rb +16 -16
  98. data/config/initializers/spree_admin_tables.rb +94 -0
  99. data/config/locales/en.yml +31 -0
  100. data/config/routes.rb +6 -6
  101. data/lib/spree/admin/engine.rb +1 -0
  102. data/lib/spree/admin/tailwind_helper.rb +11 -1
  103. data/lib/spree/admin/testing_support/tom_select.rb +1 -1
  104. metadata +30 -27
  105. data/LICENSE.md +0 -13
  106. data/app/controllers/spree/admin/custom_domains_controller.rb +0 -21
  107. data/app/controllers/spree/admin/oauth_applications_controller.rb +0 -23
  108. data/app/views/spree/admin/custom_domains/_custom_domain.html.erb +0 -11
  109. data/app/views/spree/admin/custom_domains/_custom_domains.html.erb +0 -19
  110. data/app/views/spree/admin/custom_domains/_form.html.erb +0 -7
  111. data/app/views/spree/admin/custom_domains/index.html.erb +0 -65
  112. data/app/views/spree/admin/oauth_applications/_form.html.erb +0 -6
  113. data/app/views/spree/admin/oauth_applications/_table_header.html.erb +0 -7
  114. data/app/views/spree/admin/oauth_applications/_table_row.html.erb +0 -34
  115. data/app/views/spree/admin/oauth_applications/create.turbo_stream.erb +0 -31
  116. data/app/views/spree/admin/oauth_applications/edit.html.erb +0 -1
  117. data/app/views/spree/admin/oauth_applications/index.html.erb +0 -21
  118. data/app/views/spree/admin/oauth_applications/new.html.erb +0 -1
  119. data/app/views/spree/admin/products/form/_stores.html.erb +0 -27
  120. data/app/views/spree/admin/stores/new.html.erb +0 -128
  121. data/app/views/spree/admin/stores/new.turbo_stream.erb +0 -1
  122. /data/app/views/spree/admin/{custom_domains → api_keys}/edit.html.erb +0 -0
  123. /data/app/views/spree/admin/{custom_domains → api_keys}/new.html.erb +0 -0
@@ -37,7 +37,7 @@ module Spree
37
37
 
38
38
  scope = current_store.products.not_archived.accessible_by(current_ability, :index)
39
39
  scope = scope.where.not(id: params[:omit_ids].split(',')) if params[:omit_ids].present?
40
- @products = scope.includes(master: :images, variants: :images).multi_search(query).limit(params[:limit] || 10)
40
+ @products = scope.includes(:thumbnail).multi_search(query).limit(params[:limit] || 10)
41
41
 
42
42
  respond_to do |format|
43
43
  format.turbo_stream do
@@ -69,7 +69,7 @@ module Spree
69
69
  # update the product again
70
70
  @product.slug = @product.slug_was if @product.slug.blank?
71
71
  invoke_callbacks(:update, :fails)
72
- render :edit, status: :unprocessable_entity
72
+ render :edit, status: :unprocessable_content
73
73
  end
74
74
  end
75
75
 
@@ -140,13 +140,13 @@ module Spree
140
140
  uniq.group_by(&:option_type).each_with_index do |option, index|
141
141
  option_type, option_values = option
142
142
 
143
- @product_options[option_type.id.to_s] = {
143
+ @product_options[option_type.prefixed_id] = {
144
144
  name: option_type.presentation,
145
145
  position: index + 1,
146
146
  values: option_values.map { |ov| { value: ov.name, text: ov.presentation } }.uniq
147
147
  }
148
148
 
149
- @product_available_options[option_type.id.to_s] = option_type.option_values.map { |ov| { id: ov.name, name: ov.presentation } }.uniq
149
+ @product_available_options[option_type.prefixed_id] = option_type.option_values.map { |ov| { id: ov.name, name: ov.presentation } }.uniq
150
150
  end
151
151
 
152
152
  @product_stock = {}
@@ -169,9 +169,11 @@ module Spree
169
169
  end
170
170
 
171
171
  @product_variant_ids = {}
172
+ @product_variant_prefix_ids = {}
172
173
 
173
174
  @product.variants.includes(:option_values).each do |variant|
174
175
  @product_variant_ids[variant.human_name] = variant.id.to_s
176
+ @product_variant_prefix_ids[variant.human_name] = variant.to_param
175
177
  end
176
178
  end
177
179
 
@@ -207,11 +209,16 @@ module Spree
207
209
  end
208
210
 
209
211
  # These includes are not picked automatically by ar_lazy_preload gem so we need to specify them manually.
212
+ def collection_default_sort
213
+ 'name asc'
214
+ end
215
+
210
216
  def collection_includes
211
217
  {
218
+ thumbnail: [attachment_attachment: :blob],
212
219
  stock_items: [],
213
- master: [:images, :prices, :stock_items],
214
- variants: [:images, :prices, :stock_items]
220
+ master: [:prices, :stock_items],
221
+ variants: [:prices, :stock_items]
215
222
  }
216
223
  end
217
224
 
@@ -254,7 +261,9 @@ module Spree
254
261
  return if @product.has_variants?
255
262
 
256
263
  available_stock_locations_list(master_stock_items_locations_opts).each do |_name, id|
257
- @product.master.stock_items.build(stock_location_id: id, count_on_hand: 0) unless @product.master.stock_items.find { |stock_item| stock_item.stock_location_id == id }
264
+ @product.master.stock_items.build(stock_location_id: id, count_on_hand: 0) unless @product.master.stock_items.find do |stock_item|
265
+ stock_item.stock_location_id == id
266
+ end
258
267
  end
259
268
  end
260
269
 
@@ -266,7 +275,9 @@ module Spree
266
275
  return unless Spree::Config[:product_properties_enabled]
267
276
 
268
277
  Spree::Property.all.each do |property|
269
- @product.product_properties.build(property: property) unless @product.product_properties.find { |product_property| product_property.property_id == property.id }
278
+ @product.product_properties.build(property: property) unless @product.product_properties.find do |product_property|
279
+ product_property.property_id == property.id
280
+ end
270
281
  end
271
282
  end
272
283
 
@@ -287,13 +298,11 @@ module Spree
287
298
  end
288
299
 
289
300
  def permitted_resource_params
290
- @permitted_resource_params ||= begin
291
- if cannot?(:activate, @product) && @new_status&.to_sym == :active
292
- params.require(:product).permit(permitted_product_attributes).except(:status, :make_active_at)
293
- else
294
- params.require(:product).permit(permitted_product_attributes)
295
- end
296
- end
301
+ @permitted_resource_params ||= if cannot?(:activate, @product) && @new_status&.to_sym == :active
302
+ params.require(:product).permit(permitted_product_attributes).except(:status, :make_active_at)
303
+ else
304
+ params.require(:product).permit(permitted_product_attributes)
305
+ end
297
306
  end
298
307
  end
299
308
  end
@@ -19,7 +19,7 @@ module Spree
19
19
  flash[:success] = flash_message_for(@user, :successfully_updated)
20
20
  redirect_to spree.edit_admin_profile_path
21
21
  else
22
- render :edit, status: :unprocessable_entity
22
+ render :edit, status: :unprocessable_content
23
23
  end
24
24
  end
25
25
 
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  module Admin
3
3
  class PromotionActionsController < ResourceController
4
- belongs_to 'spree/promotion', find_by: :id
4
+ belongs_to 'spree/promotion', find_by: :prefix_id
5
5
 
6
6
  helper_method :allowed_action_types
7
7
 
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  module Admin
3
3
  class PromotionRulesController < ResourceController
4
- belongs_to 'spree/promotion', find_by: :id
4
+ belongs_to 'spree/promotion', find_by: :prefix_id
5
5
 
6
6
  helper_method :allowed_rule_types
7
7
 
@@ -16,7 +16,7 @@ module Spree
16
16
 
17
17
  # POST /admin/promotions/:id/clone
18
18
  def clone
19
- promotion = current_store.promotions.find(params[:id])
19
+ promotion = current_store.promotions.find_by_prefix_id!(params[:id])
20
20
  duplicator = Spree::PromotionHandler::PromotionDuplicator.new(promotion)
21
21
 
22
22
  @new_promo = duplicator.duplicate
@@ -3,7 +3,7 @@ module Spree
3
3
  class RefundsController < ResourceController
4
4
  include Spree::Admin::OrderBreadcrumbConcern
5
5
 
6
- belongs_to 'spree/payment', find_by: :number
6
+ belongs_to 'spree/payment', find_by: :prefix_id
7
7
 
8
8
  before_action :load_order
9
9
  before_action :assign_refunder, only: :create
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  module Admin
3
3
  class ReimbursementsController < ResourceController
4
- belongs_to 'spree/order', find_by: :number
4
+ belongs_to 'spree/order', find_by: :prefix_id
5
5
 
6
6
  before_action :load_simulated_refunds, only: :edit
7
7
 
@@ -16,7 +16,7 @@ module Spree
16
16
 
17
17
  def build_resource
18
18
  if params[:build_from_customer_return_id].present?
19
- customer_return = current_store.customer_returns.find(params[:build_from_customer_return_id])
19
+ customer_return = current_store.customer_returns.find_by_prefix_id!(params[:build_from_customer_return_id])
20
20
 
21
21
  Reimbursement.build_from_customer_return(customer_return)
22
22
  else
@@ -6,6 +6,7 @@ class Spree::Admin::ResourceController < Spree::Admin::BaseController
6
6
  helper_method :new_object_url, :edit_object_url, :object_url, :collection_url, :model_class
7
7
  before_action :load_resource
8
8
  before_action :set_currency, :set_current_store, only: [:new, :create]
9
+ after_action :set_return_to, only: [:index]
9
10
 
10
11
  rescue_from ActiveRecord::RecordNotFound, with: :resource_not_found
11
12
 
@@ -50,7 +51,7 @@ class Spree::Admin::ResourceController < Spree::Admin::BaseController
50
51
  flash.now[:error] = @object.errors.full_messages.join(', ')
51
52
  end
52
53
  end
53
- format.html { render action: :edit, status: :unprocessable_entity }
54
+ format.html { render action: :edit, status: :unprocessable_content }
54
55
  end
55
56
  end
56
57
  end
@@ -81,7 +82,7 @@ class Spree::Admin::ResourceController < Spree::Admin::BaseController
81
82
  flash.now[:error] = @object.errors.full_messages.join(', ')
82
83
  end
83
84
  end
84
- format.html { render action: :new, status: :unprocessable_entity }
85
+ format.html { render action: :new, status: :unprocessable_content }
85
86
  end
86
87
  end
87
88
  end
@@ -128,7 +129,7 @@ class Spree::Admin::ResourceController < Spree::Admin::BaseController
128
129
  @parent_data ||= {}
129
130
  @parent_data[:model_name] = model_name
130
131
  @parent_data[:model_class] = model_name.to_s.classify.constantize
131
- @parent_data[:find_by] = options[:find_by] || :id
132
+ @parent_data[:find_by] = options[:find_by] || :prefix_id
132
133
  end
133
134
  end
134
135
 
@@ -187,14 +188,24 @@ class Spree::Admin::ResourceController < Spree::Admin::BaseController
187
188
  def parent
188
189
  if parent_data.present?
189
190
  base_scope = parent_data[:model_class].try(:for_store, current_store) || parent_data[:model_class]
190
-
191
- @parent ||= base_scope.
192
- # Don't use `find_by_attribute_name` to workaround globalize/globalize#423 bug
193
- send(:find_by, parent_data[:find_by].to_s => params["#{resource.model_name}_id"])
191
+ param_value = params["#{resource.model_name}_id"]
192
+ find_by = parent_data[:find_by]
193
+ parent_model_class = parent_data[:model_class]
194
+
195
+ # Skip prefix_id lookup for FriendlyId models (they use slug for to_param)
196
+ uses_friendly_id = parent_model_class.respond_to?(:friendly_id_config)
197
+ use_prefix_id = find_by == :prefix_id &&
198
+ parent_model_class.respond_to?(:_prefix_id_prefix) &&
199
+ parent_model_class._prefix_id_prefix.present? &&
200
+ !uses_friendly_id
201
+
202
+ @parent ||= if use_prefix_id
203
+ base_scope.find_by_prefix_id!(param_value)
204
+ else
205
+ base_scope.find_by!(find_by.to_s => param_value)
206
+ end
194
207
  instance_variable_set("@#{resource.model_name}", @parent)
195
208
 
196
- raise ActiveRecord::RecordNotFound if @parent.nil?
197
-
198
209
  @parent
199
210
  end
200
211
  end
@@ -204,9 +215,17 @@ class Spree::Admin::ResourceController < Spree::Admin::BaseController
204
215
  parent.send(controller_name)
205
216
  else
206
217
  model_class.try(:for_store, current_store) || model_class
207
- end
218
+ end
219
+
220
+ # Skip prefix_id lookup for FriendlyId models (they use slug for to_param)
221
+ uses_friendly_id = model_class.respond_to?(:friendly_id_config)
222
+ use_prefix_id = model_class.respond_to?(:_prefix_id_prefix) &&
223
+ model_class._prefix_id_prefix.present? &&
224
+ !uses_friendly_id
208
225
 
209
- if model_class.try(:friendly)
226
+ if use_prefix_id
227
+ base_scope.find_by_prefix_id!(params[:id])
228
+ elsif uses_friendly_id
210
229
  base_scope.friendly.find(params[:id])
211
230
  else
212
231
  base_scope.find(params[:id])
@@ -5,7 +5,7 @@ module Spree
5
5
 
6
6
  layout 'turbo_rails/frame'
7
7
 
8
- belongs_to 'spree/order', find_by: :number
8
+ belongs_to 'spree/order', find_by: :prefix_id
9
9
 
10
10
  before_action :load_variant, only: [:split, :transfer]
11
11
  before_action :refresh_shipping_rates, only: :edit
@@ -82,7 +82,7 @@ module Spree
82
82
  end
83
83
 
84
84
  def find_resource
85
- record = parent.shipments.find_by!(number: params[:id])
85
+ record = parent.shipments.find_by_prefix_id!(params[:id])
86
86
  authorize! action, record
87
87
  end
88
88
 
@@ -91,7 +91,7 @@ module Spree
91
91
  end
92
92
 
93
93
  def load_variant
94
- @variant = current_store.variants.accessible_by(current_ability, :manage).find(params[:variant_id])
94
+ @variant = current_store.variants.accessible_by(current_ability, :manage).find_by_prefix_id!(params[:variant_id])
95
95
  end
96
96
 
97
97
  def permitted_resource_params
@@ -20,7 +20,7 @@ module Spree
20
20
  end
21
21
 
22
22
  def load_data
23
- @available_zones = current_store.supported_shipping_zones
23
+ @available_zones = Spree::Zone.all
24
24
  @tax_categories = Spree::TaxCategory.order(:name)
25
25
  @calculators = Spree::ShippingMethod.calculators
26
26
  @shipping_categories = Spree::ShippingCategory.all
@@ -8,7 +8,7 @@ module Spree
8
8
  before_action :ensure_unused_store_credit, only: [:update]
9
9
 
10
10
  def show
11
- @store_credit = scope.find(params[:id])
11
+ @store_credit = scope.find_by_prefix_id!(params[:id])
12
12
  @store_credit_events = @store_credit.store_credit_events.reverse_chronological.includes(:originator, :order)
13
13
  end
14
14
 
@@ -26,7 +26,7 @@ module Spree
26
26
  redirect_to spree.admin_user_path(parent)
27
27
  else
28
28
  flash[:error] = Spree.t('store_credit.errors.unable_to_create')
29
- render :new, status: :unprocessable_entity
29
+ render :new, status: :unprocessable_content
30
30
  end
31
31
  end
32
32
 
@@ -38,7 +38,7 @@ module Spree
38
38
  redirect_to spree.admin_user_store_credit_path(parent, @store_credit)
39
39
  else
40
40
  flash[:error] = Spree.t('store_credit.errors.unable_to_update')
41
- render :edit, status: :unprocessable_entity
41
+ render :edit, status: :unprocessable_content
42
42
  end
43
43
  end
44
44
 
@@ -57,14 +57,14 @@ module Spree
57
57
  protected
58
58
 
59
59
  def parent
60
- @parent ||= Spree.user_class.find_by(id: params[:user_id])
60
+ @parent ||= Spree.user_class.find_by_prefix_id!(params[:user_id])
61
61
  end
62
62
 
63
63
  def parent_data
64
64
  {
65
65
  model_name: 'spree/user',
66
66
  model_class: Spree.user_class,
67
- find_by: :id
67
+ find_by: :prefix_id
68
68
  }
69
69
  end
70
70
 
@@ -7,32 +7,6 @@ module Spree
7
7
  before_action :normalize_supported_currencies, only: [:update]
8
8
  before_action :normalize_supported_locales, only: [:update]
9
9
  before_action :load_all_countries, only: [:edit, :update]
10
- before_action :load_all_zones, only: [:edit, :update]
11
-
12
- def new
13
- @store = Spree::Store.new(
14
- default_country_iso: current_store.default_country_iso,
15
- default_currency: current_store.default_currency
16
- )
17
- render :new, layout: 'spree/admin_wizard'
18
- end
19
-
20
- def create
21
- @store = Spree::Store.new(permitted_store_params)
22
- @store.mail_from_address = current_store.mail_from_address
23
-
24
- if @store.save
25
- # Move/copy all existing users (staff) to the new store
26
- current_store.role_users.each do |role_user|
27
- @store.add_user(role_user.user, role_user.role)
28
- end
29
-
30
- flash[:success] = flash_message_for(@store, :successfully_created)
31
- redirect_to spree.admin_getting_started_url(host: @store.url), allow_other_host: true
32
- else
33
- render :new, status: :unprocessable_entity
34
- end
35
- end
36
10
 
37
11
  def edit
38
12
  if params[:section] == 'emails'
@@ -61,7 +35,7 @@ module Spree
61
35
  flash[:error] = "#{Spree.t('store_errors.unable_to_update')}: #{@store.errors.full_messages.join(', ')}"
62
36
  end
63
37
 
64
- if @store.saved_changes? && permitted_store_params[:code].present?
38
+ if @store.saved_changes? && permitted_store_params[:code].present? && spree.respond_to?(:admin_custom_domains_url)
65
39
  redirect_to spree.admin_custom_domains_url(host: @store.url), allow_other_host: true
66
40
  else
67
41
  respond_to do |format|
@@ -87,11 +61,6 @@ module Spree
87
61
  @countries = Spree::Country.pluck(:name, :id)
88
62
  end
89
63
 
90
- def load_all_zones
91
- @zones = Spree::Zone.pluck(:name, :id)
92
- @zones.prepend([Spree.t(:no_limits_zone), ' '])
93
- end
94
-
95
64
  def normalize_supported_currencies
96
65
  if params.dig(:store, :supported_currencies)&.is_a?(Array)
97
66
  params[:store][:supported_currencies] = params[:store][:supported_currencies].compact.uniq.reject(&:blank?).join(',')
@@ -38,7 +38,7 @@ module Spree
38
38
  if @taxon.move_to_child_with_index(new_parent, new_index)
39
39
  head :ok
40
40
  else
41
- head :unprocessable_entity
41
+ head :unprocessable_content
42
42
  end
43
43
  end
44
44
 
@@ -49,7 +49,7 @@ module Spree
49
49
  end
50
50
 
51
51
  def location_after_save
52
- spree.edit_admin_taxonomy_taxon_path(@taxon.taxonomy_id, @taxon.id)
52
+ spree.edit_admin_taxonomy_taxon_path(@taxon.taxonomy, @taxon)
53
53
  end
54
54
 
55
55
  def update_turbo_stream_enabled?
@@ -113,7 +113,7 @@ module Spree
113
113
  add_breadcrumb @taxonomy.name, collection_url
114
114
 
115
115
  if @object.present? && @object.persisted?
116
- add_breadcrumb @object.name, spree.edit_admin_taxonomy_taxon_path(@taxonomy, @object.id)
116
+ add_breadcrumb @object.name, spree.edit_admin_taxonomy_taxon_path(@taxonomy, @object)
117
117
  end
118
118
  end
119
119
 
@@ -57,6 +57,7 @@ module Spree
57
57
 
58
58
  # Load available locales for this resource, excluding default
59
59
  def load_data
60
+ @default_locale = current_store.default_locale
60
61
  @locales = (current_store.supported_locales_list - [@default_locale]).sort
61
62
  end
62
63
 
@@ -58,7 +58,7 @@ module Spree
58
58
  flash[:success] = flash_message_for(@user, :successfully_created)
59
59
  redirect_to spree.admin_user_path(@user)
60
60
  else
61
- render :new, status: :unprocessable_entity
61
+ render :new, status: :unprocessable_content
62
62
  end
63
63
  end
64
64
 
@@ -92,7 +92,7 @@ module Spree
92
92
  end
93
93
 
94
94
  def find_resource
95
- model_class.accessible_by(current_ability, :show).find(params[:id])
95
+ model_class.accessible_by(current_ability, :show).find_by_prefix_id!(params[:id])
96
96
  end
97
97
 
98
98
  def collection_url
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Admin
5
+ module ApiKeysHelper
6
+ def api_key_type_options
7
+ Spree::ApiKey::KEY_TYPES.map do |type|
8
+ [Spree.t("admin.api_keys.key_types.#{type}"), type]
9
+ end
10
+ end
11
+
12
+ def api_key_status_badge(api_key)
13
+ if api_key.active?
14
+ content_tag(:span, icon('check') + Spree.t('admin.api_keys.statuses.active'), class: 'badge badge-success')
15
+ else
16
+ content_tag(:span, icon('alert-triangle') + Spree.t('admin.api_keys.statuses.revoked'), class: 'badge badge-danger')
17
+ end
18
+ end
19
+
20
+ def api_key_type_badge(api_key)
21
+ badge_class = api_key.publishable? ? 'badge-light' : 'badge-warning'
22
+ icon = api_key.publishable? ? 'eye' : 'lock-password'
23
+ content_tag(:span, icon(icon) + Spree.t("admin.api_keys.key_types.#{api_key.key_type}"), class: "badge #{badge_class}")
24
+ end
25
+
26
+ def masked_api_key(api_key)
27
+ token = api_key.token
28
+ "#{token[0..6]}#{'*' * 16}#{token[-4..]}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -214,7 +214,7 @@ module Spree
214
214
  case key
215
215
  when :currency
216
216
  content_tag(:div, form.label("preferred_#{key}", Spree.t(key, scope: i18n_scope)) +
217
- form.select("preferred_#{key}", current_store.supported_currencies.split(','), {}, { data: { controller: 'autocomplete-select' }, disabled: current_store.supported_currencies.split(',').count == 1 }),
217
+ form.select("preferred_#{key}", current_store.supported_currencies_list, {}, { data: { controller: 'autocomplete-select' } }),
218
218
  class: 'form-group', id: [object.class.to_s.parameterize, 'preference', key].join('-'))
219
219
  else
220
220
  if object.preference_type(key).to_sym == :boolean
@@ -361,6 +361,11 @@ module Spree
361
361
  return Spree.t(:none) if zone_ids.empty?
362
362
 
363
363
  Spree::Zone.where(id: zone_ids).pluck(:name).join(', ')
364
+ when :market_ids
365
+ market_ids = Array(value).reject(&:blank?).map(&:to_i)
366
+ return Spree.t(:none) if market_ids.empty?
367
+
368
+ Spree::Market.where(id: market_ids).pluck(:name).join(', ')
364
369
  when :user_ids
365
370
  user_ids = Array(value).reject(&:blank?).map(&:to_i)
366
371
  return Spree.t(:none) if user_ids.empty?
@@ -18,59 +18,63 @@ module Spree
18
18
  end
19
19
 
20
20
  def json_serializers_available?(record)
21
- storefront_serializer_exists?(record) || platform_serializer_exists?(record)
21
+ store_serializer_exists?(record) || admin_serializer_exists?(record)
22
22
  end
23
23
 
24
- def storefront_serializer_exists?(record)
25
- storefront_serializer_for(record).present?
24
+ def store_serializer_exists?(record)
25
+ store_serializer_for(record).present?
26
26
  end
27
27
 
28
- def platform_serializer_exists?(record)
29
- platform_serializer_for(record).present?
28
+ def admin_serializer_exists?(record)
29
+ admin_serializer_for(record).present?
30
30
  end
31
31
 
32
- def serialize_to_json(record, api_type: :storefront)
32
+ def serialize_to_json(record, api_type: :store)
33
33
  return unless record
34
34
 
35
35
  serializer = case api_type.to_sym
36
- when :storefront
37
- storefront_serializer_for(record)
38
- when :platform
39
- platform_serializer_for(record)
36
+ when :store
37
+ store_serializer_for(record)
38
+ when :admin
39
+ admin_serializer_for(record)
40
40
  end
41
41
 
42
42
  return nil unless serializer
43
43
 
44
- serializable_hash = serializer.new(
44
+ # Alba serializers use .new(object, params: {}).to_h
45
+ serialized_hash = serializer.new(
45
46
  record,
46
47
  params: serializer_params(record, api_type)
47
- ).serializable_hash
48
+ ).to_h
48
49
 
49
- JSON.pretty_generate(serializable_hash)
50
+ JSON.pretty_generate(serialized_hash)
50
51
  end
51
52
 
52
53
  private
53
54
 
54
- def storefront_serializer_for(record)
55
- serializer_for(record, api_type: :storefront, namespace: 'Spree::V2::Storefront')
55
+ def store_serializer_for(record)
56
+ serializer_for(record, namespace: 'Spree::Api::V3')
56
57
  end
57
58
 
58
- def platform_serializer_for(record)
59
- serializer_for(record, api_type: :platform, namespace: 'Spree::Api::V2::Platform')
59
+ def admin_serializer_for(record)
60
+ serializer_for(record, namespace: 'Spree::Api::V3::Admin')
60
61
  end
61
62
 
62
- def serializer_for(record, api_type:, namespace:)
63
+ def serializer_for(record, namespace:)
63
64
  class_name = record.class.name.demodulize
64
- method_name = "#{api_type}_#{class_name.underscore}_serializer"
65
- if Spree.api.respond_to?(method_name)
66
- Spree.api.public_send(method_name)
67
- else
68
- serializer_class_name = "#{namespace}::#{class_name}Serializer"
69
- serializer_class_name.safe_constantize
65
+
66
+ # First try dependency lookup (without prefix for v3)
67
+ method_name = "#{class_name.underscore}_serializer"
68
+ if namespace == 'Spree::Api::V3' && Spree.api.respond_to?(method_name)
69
+ return Spree.api.public_send(method_name)
70
70
  end
71
+
72
+ # Fall back to direct constant lookup
73
+ serializer_class_name = "#{namespace}::#{class_name}Serializer"
74
+ serializer_class_name.safe_constantize
71
75
  end
72
76
 
73
- def serializer_params(record, api_type)
77
+ def serializer_params(_record, api_type)
74
78
  params = {}
75
79
  params[:api_type] = api_type
76
80
  params[:store] = current_store if defined?(current_store) && current_store.present?
@@ -37,7 +37,7 @@ module Spree
37
37
  end
38
38
 
39
39
  def load_user
40
- @user = Spree.user_class.find(params[:user_id]) if params[:user_id].present?
40
+ @user = Spree.user_class.find_by_prefix_id(params[:user_id]) if params[:user_id].present?
41
41
  end
42
42
  end
43
43
  end
@@ -23,7 +23,7 @@ module Spree
23
23
  class: 'sortable-tree-item draggable',
24
24
  data: {
25
25
  sortable_tree_resource_name_value: :taxon,
26
- sortable_tree_update_url_value: spree.reposition_admin_taxonomy_taxon_path(child_resource.taxonomy_id, child_resource.id)
26
+ sortable_tree_update_url_value: spree.reposition_admin_taxonomy_taxon_path(child_resource.taxonomy, child_resource)
27
27
  })
28
28
  end
29
29
  end
@@ -3,10 +3,6 @@ module Spree
3
3
  module StoresHelper
4
4
  include Spree::ImagesHelper
5
5
 
6
- def available_stores
7
- @available_stores ||= Spree::Store.accessible_by(current_ability, :manage).includes(:logo_attachment, :favicon_image_attachment, :default_custom_domain)
8
- end
9
-
10
6
  DEFAULT_ICON_SIZE = 40
11
7
 
12
8
  def store_admin_icon(store, opts = {})
@@ -19,7 +19,11 @@ export default class extends Controller {
19
19
  maxOptions: this.element.children.length,
20
20
  selectOnTab: true,
21
21
  allowEmptyOption: false,
22
- plugins: plugins
22
+ plugins: plugins,
23
+ onItemAdd: function() {
24
+ this.setTextboxValue('')
25
+ this.refreshOptions(false)
26
+ }
23
27
  })
24
28
  if (!this.element.multiple) {
25
29
  this.select.on('type', () => {
@@ -3,7 +3,7 @@ import { Controller } from "@hotwired/stimulus"
3
3
  export default class extends Controller {
4
4
  static targets = ["input", "clear"]
5
5
 
6
- inputTargetConnected() {
6
+ connect() {
7
7
  this.toggleClearButton()
8
8
  }
9
9
 
@@ -106,6 +106,10 @@ export default class extends Controller {
106
106
  if (this.multipleValue) {
107
107
  settings.maxItems = null
108
108
  settings.plugins = ['remove_button']
109
+ settings.onItemAdd = function() {
110
+ this.setTextboxValue('')
111
+ this.refreshOptions(false)
112
+ }
109
113
  if (this.activeOptionValue) settings.items = JSON.parse(this.activeOptionValue)
110
114
  } else {
111
115
  if (this.activeOptionValue) settings.items = [this.activeOptionValue]