spree_storefront 5.2.6 → 5.3.0.rc1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +5 -3
  3. data/app/controllers/concerns/spree/cart_methods.rb +0 -1
  4. data/app/controllers/concerns/spree/storefront/pagination_concern.rb +23 -0
  5. data/app/controllers/spree/account/gift_cards_controller.rb +1 -1
  6. data/app/controllers/spree/account/orders_controller.rb +1 -1
  7. data/app/controllers/spree/account/store_credits_controller.rb +4 -4
  8. data/app/controllers/spree/posts_controller.rb +1 -1
  9. data/app/controllers/spree/products_controller.rb +1 -0
  10. data/app/controllers/spree/search_controller.rb +1 -1
  11. data/app/controllers/spree/store_controller.rb +26 -32
  12. data/app/helpers/spree/page_helper.rb +2 -4
  13. data/app/helpers/spree/products_helper.rb +30 -7
  14. data/app/helpers/spree/storefront_helper.rb +2 -1
  15. data/app/helpers/spree/taxons_helper.rb +3 -1
  16. data/app/helpers/spree/theme_helper.rb +1 -1
  17. data/app/views/devise/sessions/new.html.erb +1 -1
  18. data/app/views/spree/checkout/_address.html.erb +2 -2
  19. data/app/views/spree/checkout/_coupon_code.html.erb +2 -2
  20. data/app/views/spree/checkout/_delivery.html.erb +2 -2
  21. data/app/views/spree/checkout/_delivery_shipping_rate.html.erb +1 -1
  22. data/app/views/spree/checkout/_payment_methods.html.erb +1 -1
  23. data/app/views/spree/products/index.html.erb +1 -1
  24. data/app/views/spree/search/show.html.erb +1 -1
  25. data/app/views/spree/shared/_load_more_products.turbo_stream.erb +1 -1
  26. data/app/views/spree/shared/_product_listing_page.html.erb +2 -2
  27. data/app/views/themes/default/spree/page_sections/_featured_product.html.erb +4 -4
  28. data/app/views/themes/default/spree/page_sections/_featured_taxons.html.erb +4 -4
  29. data/app/views/themes/default/spree/page_sections/_post_grid.html.erb +1 -1
  30. data/app/views/themes/default/spree/page_sections/_product_details.html.erb +2 -2
  31. data/app/views/themes/default/spree/posts/_pagination.html.erb +1 -1
  32. data/app/views/themes/default/spree/products/_add_to_cart_button.html.erb +16 -3
  33. data/app/views/themes/default/spree/products/_color_swatches.html.erb +1 -1
  34. data/app/views/themes/default/spree/products/_featured_image.html.erb +2 -1
  35. data/app/views/themes/default/spree/products/_json_ld.html.erb +4 -4
  36. data/app/views/themes/default/spree/products/_price.html.erb +3 -1
  37. data/app/views/themes/default/spree/products/_product.html.erb +3 -3
  38. data/app/views/themes/default/spree/products/_show_more_button.html.erb +3 -3
  39. data/app/views/themes/default/spree/shared/_pagination.html.erb +7 -0
  40. data/lib/generators/spree/storefront/install/install_generator.rb +12 -4
  41. data/lib/generators/spree/storefront/install/templates/application.css +44 -0
  42. data/lib/spree/storefront/configuration.rb +3 -0
  43. data/lib/spree/storefront.rb +3 -1
  44. metadata +38 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c186ef5bda630498c3468d11a61144b479179026bb2178d341b1b10d85ede273
4
- data.tar.gz: a83841e5ed48fb94a94511b5b6bf849b56ddbea3ed2ba3039e3b7854e17f52fc
3
+ metadata.gz: 2d2469dfa268f4b5001def6a8efc6c6c69ee8e1cfcbec059f09b1b369f99f003
4
+ data.tar.gz: cbf4c3c04197c8c9b283efc23e04fe886b035159603e40ef36a30eb05a260252
5
5
  SHA512:
6
- metadata.gz: 169a0eead13f4e94330d83d93a30e5b66734d8d0a83b40365c24588649c02e040859a611636e1e90c11a119295fcac06f263b4ff89a6529b7795ff89872393ca
7
- data.tar.gz: 793f3e4698d50a1f271225ac10cd2f2033774a3bf38bcfb76a8493bdb4c9ba9eec123a5e6d82ae2ec23fc10db6185a911dd1821b279c4795614cafd2bbdaee5b
6
+ metadata.gz: 1bc5443c86a416f3d3145f5da3e5c19581c0e1955ca66dddfe680a9a3f81cf60b412acc5fb43dcc8af1e51b11e4ea1bb499cd1c6ba7347f22c4d577c56aed667
7
+ data.tar.gz: 78cc85fcae54894e3df300517534ee7163df515eb1cc76ac6ddab2de2a45ea0049ad903b20890da119d6605c1488e4b3e540eabf6d696445ba4c37c6d61001d9
data/Rakefile CHANGED
@@ -13,9 +13,11 @@ task :test_app do
13
13
  ENV['LIB_NAME'] = 'spree/storefront'
14
14
 
15
15
  Rake::Task['common:test_app'].execute(
16
- user_class: 'User',
16
+ user_class: 'Spree::User',
17
+ admin_user_class: 'Spree::AdminUser',
17
18
  authentication: 'devise',
18
- install_storefront: 'true',
19
- install_admin: 'false'
19
+ install_admin: true,
20
+ css: true,
21
+ javascript: true
20
22
  )
21
23
  end
@@ -44,7 +44,6 @@ module Spree
44
44
  { option_values: [:option_type] },
45
45
  {
46
46
  product: [
47
- :variant_images,
48
47
  { master: :images },
49
48
  { variants: :images }
50
49
  ]
@@ -0,0 +1,23 @@
1
+ module Spree
2
+ module Storefront
3
+ module PaginationConcern
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include Pagy::Method
8
+ end
9
+
10
+ private
11
+
12
+ def paginate_collection(collection, limit:)
13
+ if Spree::Storefront::Config[:use_kaminari_pagination]
14
+ collection.page(params[:page]).per(limit)
15
+ else
16
+ # Uses countish paginator which is faster as it avoids COUNT queries
17
+ @pagy, records = pagy(:countish, collection, limit: limit)
18
+ records
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,7 +2,7 @@ module Spree
2
2
  module Account
3
3
  class GiftCardsController < BaseController
4
4
  def index
5
- @gift_cards = @user.gift_cards.order(created_at: :desc).page(params[:page]).per(25)
5
+ @gift_cards = paginate_collection(@user.gift_cards.order(created_at: :desc), limit: 25)
6
6
  end
7
7
 
8
8
  private
@@ -5,7 +5,7 @@ module Spree
5
5
 
6
6
  # GET /account/orders
7
7
  def index
8
- @orders = orders_scope.order(created_at: :desc).page(params[:page]).per(25)
8
+ @orders = paginate_collection(orders_scope.order(created_at: :desc), limit: 25)
9
9
  end
10
10
 
11
11
  # GET /account/orders/:id
@@ -3,10 +3,10 @@ module Spree
3
3
  class StoreCreditsController < BaseController
4
4
  # GET /account/store_credits
5
5
  def index
6
- @store_credit_events = Spree::StoreCreditEvent.where(store_credit: user_store_credits).
7
- exposed_events.
8
- reverse_chronological.
9
- page(params[:page]).per(25)
6
+ events = Spree::StoreCreditEvent.where(store_credit: user_store_credits).
7
+ exposed_events.
8
+ reverse_chronological
9
+ @store_credit_events = paginate_collection(events, limit: 25)
10
10
  end
11
11
 
12
12
  private
@@ -13,7 +13,7 @@ module Spree
13
13
  scope = scope.where(post_category_id: @category.id) if @category.present?
14
14
 
15
15
  sorted_posts = scope.order(published_at: :desc)
16
- @posts = sorted_posts.page(params[:page]).per(20)
16
+ @posts = paginate_collection(sorted_posts, limit: 20)
17
17
  end
18
18
 
19
19
  def show
@@ -25,6 +25,7 @@ module Spree
25
25
  # when using not default locale, our related products are different for different locales.
26
26
  @products = storefront_products_scope.where.not(id: @product.id).
27
27
  multi_search(@product.name).includes(storefront_products_includes).
28
+ preload_associations_lazily.
28
29
  limit(@section.preferred_max_products_to_show)
29
30
  end
30
31
 
@@ -14,7 +14,7 @@ module Spree
14
14
 
15
15
  if query.present? && query.length >= Spree::Storefront::Config.search_min_query_length
16
16
  products_scope = storefront_products_scope.multi_search(query)
17
- @products = products_scope.includes(storefront_products_includes)
17
+ @products = products_scope.includes(storefront_products_includes).preload_associations_lazily
18
18
  @taxons = current_store.taxons.search_by_name(query)
19
19
  end
20
20
  end
@@ -9,6 +9,7 @@ module Spree
9
9
  include Spree::WishlistHelper
10
10
  include Spree::AnalyticsHelper
11
11
  include Spree::IntegrationsHelper
12
+ include Spree::Storefront::PaginationConcern
12
13
 
13
14
  layout :choose_layout
14
15
 
@@ -28,7 +29,7 @@ module Spree
28
29
  helper_method :current_taxon, :store_filter_names
29
30
 
30
31
  helper_method :permitted_products_params, :products_filters_params,
31
- :storefront_products_scope, :storefront_products,
32
+ :storefront_products_scope, :storefront_products, :storefront_pagy,
32
33
  :default_products_sort, :default_products_finder_params,
33
34
  :storefront_products_includes, :storefront_products_finder
34
35
 
@@ -157,38 +158,36 @@ module Spree
157
158
  end
158
159
 
159
160
  def storefront_products
160
- @storefront_products ||= begin
161
- finder_params = default_products_finder_params
162
- finder_params[:sort_by] ||= @taxon&.sort_order || 'manual'
161
+ return @storefront_products if @storefront_products
163
162
 
164
- products_finder = storefront_products_finder
165
- products = products_finder.
166
- new(scope: storefront_products_scope, params: finder_params).
167
- execute.
168
- includes(storefront_products_includes)
163
+ finder_params = default_products_finder_params
164
+ finder_params[:sort_by] ||= @taxon&.sort_order || 'manual'
169
165
 
170
- default_per_page = Spree::Storefront::Config[:products_per_page]
171
- per_page = params[:per_page].present? ? params[:per_page].to_i : default_per_page
172
- page = params[:page].present? ? params[:page].to_i : 1
166
+ products_finder = storefront_products_finder
167
+ products = products_finder.
168
+ new(scope: storefront_products_scope, params: finder_params).
169
+ execute.
170
+ includes(storefront_products_includes).
171
+ preload_associations_lazily
173
172
 
174
- products.page(page).per(per_page)
175
- end
173
+ default_per_page = Spree::Storefront::Config[:products_per_page]
174
+ per_page = params[:per_page].present? ? params[:per_page].to_i : default_per_page
175
+
176
+ @storefront_products = paginate_collection(products, limit: per_page)
177
+ end
178
+
179
+ def storefront_pagy
180
+ storefront_products unless @storefront_products
181
+ @pagy
176
182
  end
177
183
 
184
+ # These includes are not picked automatically by ar_lazy_preload gem so we need to specify them manually.
178
185
  def storefront_products_includes
179
- [
180
- :prices_including_master,
181
- :variant_images,
182
- :option_types,
183
- :option_values,
184
- { master: [:images, :prices, :stock_items, :stock_locations, { stock_items: :stock_location }],
185
- variants: [
186
- :images, :prices, :option_values, :stock_items, :stock_locations,
187
- { option_values: :option_type, stock_items: :stock_location }
188
- ],
189
- taxons: [:taxonomy],
190
- taggings: [:tag] }
191
- ]
186
+ {
187
+ taxons: [],
188
+ master: [:images, :prices, { stock_items: :stock_location }],
189
+ variants: [:images, :prices, { stock_items: :stock_location }]
190
+ }
192
191
  end
193
192
 
194
193
  def redirect_unauthorized_access
@@ -205,11 +204,6 @@ module Spree
205
204
  end
206
205
  end
207
206
 
208
- def redirect_back_or_default(default)
209
- Spree::Deprecation.warn('redirect_back_or_default is deprecated and will be removed in Spree 5.2. Please use redirect_back(fallback_location: default) instead.')
210
- redirect_back(fallback_location: default)
211
- end
212
-
213
207
  def require_user(return_to: nil, redirect_path: nil)
214
208
  return if try_spree_current_user
215
209
 
@@ -12,7 +12,7 @@ module Spree
12
12
  page ||= current_page
13
13
 
14
14
  sections = current_page_preview.present? ? current_page_preview.sections : page.sections
15
- sections_html = sections.includes(section_includes).map do |section|
15
+ sections_html = sections.includes(section_includes).preload_associations_lazily.map do |section|
16
16
  render_section(section, variables)
17
17
  end.join.html_safe
18
18
 
@@ -98,9 +98,7 @@ module Spree
98
98
  # @return [Array] the includes for the section
99
99
  def section_includes
100
100
  [
101
- :links, :rich_text_text,
102
- :rich_text_description, { asset_attachment: :blob },
103
- { blocks: [:rich_text_text, :links] }
101
+ { asset_attachment: :blob }
104
102
  ]
105
103
  end
106
104
 
@@ -15,15 +15,38 @@ module Spree
15
15
  ]
16
16
  end
17
17
 
18
- def should_display_compare_at_price?(product_or_variant)
19
- price = product_or_variant.price_in(current_currency)
20
- price.compare_at_amount.present? && (price.compare_at_amount > price.amount)
18
+ # Builds a pricing context for a variant with current request context
19
+ #
20
+ # @param product_or_variant [Spree::Product, Spree::Variant] The product or variant to build context for
21
+ # @param options [Hash] Optional overrides for context attributes
22
+ # @option options [String] :currency Override the currency (defaults to current_currency)
23
+ # @option options [Spree::Store] :store Override the store (defaults to current_store)
24
+ # @option options [Spree::Zone] :zone Override the zone (defaults to current_order.tax_zone || current_store.checkout_zone)
25
+ # @option options [Spree::User] :user Override the user (defaults to try_spree_current_user)
26
+ # @option options [Integer] :quantity Specify quantity for volume pricing
27
+ # @option options [Time] :date Specify date for date-based pricing
28
+ # @option options [Spree::Order] :order Specify the order
29
+ # @return [Spree::Pricing::Context] The pricing context
30
+ def pricing_context_for_variant(product_or_variant, **options)
31
+ variant = product_or_variant.is_a?(Spree::Product) ? product_or_variant.default_variant : product_or_variant
32
+
33
+ Spree::Pricing::Context.new(
34
+ variant: variant,
35
+ currency: options[:currency] || current_currency,
36
+ store: options[:store] || current_store,
37
+ zone: options[:zone] || current_order&.tax_zone || current_store.checkout_zone,
38
+ user: options[:user] || try_spree_current_user,
39
+ quantity: options[:quantity],
40
+ date: options[:date],
41
+ order: options[:order] || current_order
42
+ )
21
43
  end
22
44
 
23
- def weeks_online(product)
24
- Spree::Deprecation.warn('weeks_online is deprecated and will be removed in Spree 5.2')
25
-
26
- (Time.current - product.activated_at.in_time_zone(current_store.preferred_timezone)).seconds.in_weeks.to_i.abs
45
+ def should_display_compare_at_price?(product_or_variant)
46
+ variant = product_or_variant.is_a?(Spree::Product) ? product_or_variant.default_variant : product_or_variant
47
+ context = pricing_context_for_variant(variant)
48
+ price = variant.price_for(context)
49
+ price.compare_at_amount.present? && (price.compare_at_amount > price.amount)
27
50
  end
28
51
 
29
52
  def product_not_selected_options(product, selected_variant, options_param_name: :options)
@@ -12,7 +12,8 @@ module Spree
12
12
  @spree_storefront_base_cache_key ||= [
13
13
  spree_base_cache_key,
14
14
  current_wishlist,
15
- current_order
15
+ current_order,
16
+ Spree::Current.price_lists.cache_key_with_version
16
17
  ].compact
17
18
  end
18
19
 
@@ -12,7 +12,9 @@ module Spree
12
12
  sort_by: 'default'
13
13
  }
14
14
 
15
- storefront_products_finder.new(scope: current_store.products.includes(storefront_products_includes), params: finder_params).execute
15
+ scope = current_store.products.includes(storefront_products_includes).preload_associations_lazily
16
+
17
+ storefront_products_finder.new(scope: scope, params: finder_params).execute
16
18
  end
17
19
  end
18
20
  end
@@ -79,7 +79,7 @@ module Spree
79
79
  #
80
80
  # @return [Hash] the theme layout sections
81
81
  def theme_layout_sections
82
- Spree::Deprecation.warn('theme_layout_sections is deprecated and will be removed in Spree 6.0. Please use render_header_sections and render_footer_sections instead.')
82
+ Spree::Deprecation.warn('theme_layout_sections is deprecated and will be removed in Spree 5.5. Please use render_header_sections and render_footer_sections instead.')
83
83
 
84
84
  @theme_layout_sections ||= current_theme_or_preview.sections.includes(section_includes).all.each_with_object({}) do |section, hash|
85
85
  hash[section.type.to_s.demodulize.underscore] = section
@@ -33,7 +33,7 @@
33
33
  </div>
34
34
 
35
35
  <div class="flex flex-col items-center gap-4">
36
- <p class="text-sm text-gray-600"><%= Spree.t(:continue_without_logging_in) %></p>
36
+ <p class="text-sm text-muted"><%= Spree.t(:continue_without_logging_in) %></p>
37
37
  <%= link_to Spree.t(:continue_as_guest), spree.checkout_path(current_order.token, guest: true), class: 'btn btn-secondary w-full max-w-sm', data: { turbo: false } %>
38
38
  </div>
39
39
  <% end %>
@@ -73,7 +73,7 @@
73
73
  </h5>
74
74
  <% if !already_have_an_account? && defined?(spree_login_path) %>
75
75
  <div class="lg:text-right">
76
- <span class="text-gray-600 text-sm">
76
+ <span class="text-muted text-sm">
77
77
  <%= Spree.t('storefront.checkout.already_have_an_account') %> <span class="text-primary"><%= link_to Spree.t(:login), spree_login_path, data: { turbo_frame: '_top' } %></span>
78
78
  </span>
79
79
  </div>
@@ -134,7 +134,7 @@
134
134
  <%= render 'spree/checkout/special_instructions', form: form %>
135
135
 
136
136
  </div>
137
- <div class="flex justify-end w-full">
137
+ <div class="flex justify-end w-full gap-2">
138
138
  <%= button_tag Spree.t(:save_and_continue),
139
139
  class: 'btn-primary w-full lg:w-2/5 font-semibold checkout-content-save-continue-button my-5 !py-4',
140
140
  data: {
@@ -16,7 +16,7 @@
16
16
  <%= hidden_field_tag :coupon_code, promotion.code_for_order(@order) %>
17
17
  <div class="m-0">
18
18
  <div class="flex items-center justify-between text-sm">
19
- <span class="max-w-[250px] truncate flex items-center p-2 rounded-sm bg-gray-200">
19
+ <span class="max-w-[250px] truncate flex items-center p-2 rounded-sm bg-border">
20
20
  <%= heroicon('tag', options: { class: 'h-4 w-4 mr-2' }) %>
21
21
  <%= promotion.name_for_order(@order) %>
22
22
  </span>
@@ -31,7 +31,7 @@
31
31
  <%= hidden_field_tag :gift_card, @order.gift_card.code %>
32
32
  <div class="m-0">
33
33
  <div class="flex items-center justify-between text-sm">
34
- <span class="max-w-[250px] truncate flex items-center p-2 rounded-sm bg-gray-200">
34
+ <span class="max-w-[250px] truncate flex items-center p-2 rounded-sm bg-border">
35
35
  <%= heroicon('gift', options: { class: 'h-4 w-4 mr-2' }) %>
36
36
  <%= @order.gift_card.code.upcase %>
37
37
  </span>
@@ -24,8 +24,8 @@
24
24
  </div>
25
25
  </div>
26
26
  </div>
27
- <div class="flex justify-end w-full mt-5">
28
- <div class="flex justify-end w-full">
27
+ <div class="flex justify-end w-full gap-2 mt-5">
28
+ <div class="flex justify-end w-full gap-2">
29
29
  <%= button_tag Spree.t(:save_and_continue),
30
30
  class: 'btn-primary w-full lg:w-2/5 font-semibold checkout-content-save-continue-button my-5 !py-4',
31
31
  data: { action: 'click->checkout-delivery#disableShippingRates',
@@ -12,7 +12,7 @@
12
12
  <label class="select-none cursor-pointer px-2 word-break w-full" for="<%= "shipping-rate-#{rate.id}" %>">
13
13
  <%= rate.name %>
14
14
  <% if rate.delivery_range %>
15
- <span class="text-gray-600 text-xs ml-3"><%= rate.display_delivery_range %></span>
15
+ <span class="text-muted text-xs ml-3"><%= rate.display_delivery_range %></span>
16
16
  <% end %>
17
17
  </label>
18
18
  <span class="text-right">
@@ -39,7 +39,7 @@
39
39
 
40
40
  <%= render 'spree/checkout/terms_and_conditions' %>
41
41
 
42
- <div class="flex justify-end w-full">
42
+ <div class="flex justify-end w-full gap-2">
43
43
  <button type="submit"
44
44
  id="checkout-payment-submit"
45
45
  class="btn-primary w-full lg:w-2/5 font-semibold checkout-content-save-continue-button my-5 !py-4"
@@ -1 +1 @@
1
- <%= render_page(current_page, products: storefront_products.records, sort: products_sort) %>
1
+ <%= render_page(current_page, products: storefront_products, sort: products_sort) %>
@@ -1,6 +1,6 @@
1
1
  <%= render_page(
2
2
  current_page,
3
- products: storefront_products.records,
3
+ products: storefront_products,
4
4
  query: query,
5
5
  sort: products_sort
6
6
  ) %>
@@ -3,5 +3,5 @@
3
3
  <% end %>
4
4
 
5
5
  <%= turbo_stream.append "products" do %>
6
- <%= render 'spree/shared/products', products: storefront_products.records %>
6
+ <%= render 'spree/shared/products', products: storefront_products %>
7
7
  <% end %>
@@ -1,11 +1,11 @@
1
1
  <% if turbo_frame_request? && current_theme_preview.nil? %>
2
- <%= render 'spree/page_sections/product_grid', products: storefront_products.records, section: current_page.sections.find_by(type: 'Spree::PageSections::ProductGrid') %>
2
+ <%= render 'spree/page_sections/product_grid', products: storefront_products, section: current_page.sections.find_by(type: 'Spree::PageSections::ProductGrid') %>
3
3
  <% else %>
4
4
  <%= render_page(
5
5
  current_page,
6
6
  taxon: @taxon,
7
7
  collection: @collection,
8
- products: storefront_products.records,
8
+ products: storefront_products,
9
9
  ) %>
10
10
  <% end %>
11
11
 
@@ -47,15 +47,15 @@
47
47
  <div <%= block_attributes(block) %>>
48
48
  <% case block.type %>
49
49
  <% when 'Spree::PageBlocks::Products::Brand' %>
50
- <% if presenter.product&.brand&.page_builder_url %>
51
- <%= link_to spree_storefront_resource_url(presenter.product&.brand), title: strip_tags(presenter.product&.brand&.name).strip, data: { turbo_frame: '_top' } do %>
50
+ <% if presenter.product&.brand_taxon&.page_builder_url %>
51
+ <%= link_to spree_storefront_resource_url(presenter.product&.brand_taxon), title: strip_tags(presenter.product&.brand_taxon&.name).strip, data: { turbo_frame: '_top' } do %>
52
52
  <h3 class="text-sm uppercase font-semibold">
53
- <%= presenter.product&.brand&.name %>
53
+ <%= presenter.product&.brand_taxon&.name %>
54
54
  </h3>
55
55
  <% end %>
56
56
  <% else %>
57
57
  <h3 class="text-sm uppercase font-semibold">
58
- <%= presenter.product&.brand&.name %>
58
+ <%= presenter.product&.brand_taxon&.name %>
59
59
  </h3>
60
60
  <% end %>
61
61
  <% when 'Spree::PageBlocks::Products::Title' %>
@@ -14,11 +14,11 @@
14
14
  <%= page_builder_link_to link, title: link.label, class: 'group block overflow-hidden', target: link.open_in_new_tab.presence && '_blank' do %>
15
15
  <% if link.linkable.page_builder_image&.attached? && link.linkable.page_builder_image&.variable? %>
16
16
  <div class="flex space-y-2 flex-col">
17
- <%= spree_image_tag(link.linkable.page_builder_image, height: 300, width: 300, class: 'h-full w-full object-cover object-center group-hover:opacity-75 rounded-md bg-gray-200', loading: :lazy, alt: link.label) %>
17
+ <%= spree_image_tag(link.linkable.page_builder_image, height: 300, width: 300, class: 'h-full w-full object-cover object-center group-hover:opacity-75 rounded-md bg-border', loading: :lazy, alt: link.label) %>
18
18
  <span><%= link.label %></span>
19
19
  </div>
20
20
  <% else %>
21
- <div class="aspect-1 w-full group-hover:bg-gray-100 bg-gray-200 rounded-md flex items-center justify-center relative">
21
+ <div class="aspect-1 w-full group-hover:bg-gray-100 bg-border rounded-md flex items-center justify-center relative">
22
22
  <span><%= link.label %></span>
23
23
  </div>
24
24
  <% end %>
@@ -49,10 +49,10 @@
49
49
  <%= page_builder_link_to link, title: link.label, class: 'group block overflow-hidden', target: link.open_in_new_tab.presence && '_blank' do %>
50
50
  <% if link.linkable.page_builder_image.attached? %>
51
51
  <div class="flex space-y-2 flex-col">
52
- <%= spree_image_tag(link.linkable.page_builder_image, height: 200, width: 200, class: 'h-full w-full object-cover object-center group-hover:opacity-75 rounded-md bg-gray-200', loading: :lazy, alt: link.label) %>
52
+ <%= spree_image_tag(link.linkable.page_builder_image, height: 200, width: 200, class: 'h-full w-full object-cover object-center group-hover:opacity-75 rounded-md bg-border', loading: :lazy, alt: link.label) %>
53
53
  </div>
54
54
  <% else %>
55
- <div class="aspect-1 w-full group-hover:bg-gray-100 bg-gray-200 flex items-center justify-center relative">
55
+ <div class="aspect-1 w-full group-hover:bg-gray-100 bg-border flex items-center justify-center relative">
56
56
  <span><%= link.label %></span>
57
57
  </div>
58
58
  <% end %>
@@ -5,7 +5,7 @@
5
5
  <%= render partial: 'spree/posts/post', collection: @posts, cached: true %>
6
6
  </div>
7
7
 
8
- <%= paginate @posts, theme: 'storefront', outer_window: 1, inner_window: 2 %>
8
+ <%= render 'spree/shared/pagination' %>
9
9
  </div>
10
10
  <% end %>
11
11
  </div>
@@ -47,9 +47,9 @@
47
47
  </h1>
48
48
  <% when 'Spree::PageBlocks::Products::Brand' %>
49
49
  <% if product.brand_taxon %>
50
- <%= link_to spree.nested_taxons_path(product.brand_taxon), title: product.brand_name do %>
50
+ <%= link_to spree.nested_taxons_path(product.brand_taxon), title: product.brand_taxon.name do %>
51
51
  <h3 class="text-sm lg:mt-0 inline-block mb-1">
52
- <%= product.brand_name %>
52
+ <%= product.brand_taxon.name %>
53
53
  </h3>
54
54
  <% end %>
55
55
  <% end %>
@@ -1 +1 @@
1
- <%= paginate @posts, theme: 'storefront', outer_window: 1, inner_window: 2 %>
1
+ <%= render 'spree/shared/pagination' %>
@@ -5,11 +5,24 @@ options_param_name ||= :options
5
5
  not_selected_options = product_not_selected_options(product, selected_variant, options_param_name: options_param_name)
6
6
  all_options_selected = (params[options_param_name].present? || @variant_from_options.present?) && not_selected_options.empty?
7
7
  not_all_options_selected = !all_options_selected && product.any_variant_available?(current_currency)
8
+
9
+ # Build pricing context for selected variant
10
+ if selected_variant
11
+ variant_pricing_context = pricing_context_for_variant(selected_variant)
12
+ variant_price = selected_variant.price_for(variant_pricing_context)
13
+ else
14
+ variant_price = nil
15
+ end
16
+
8
17
  variant_not_available = selected_variant.nil? ||
9
18
  product.discontinued? ||
10
19
  selected_variant.discontinued? ||
11
20
  !selected_variant.purchasable? ||
12
- selected_variant.price_in(current_currency).amount.nil?
21
+ variant_price&.amount.nil?
22
+
23
+ # Build pricing context for product
24
+ product_pricing_context = pricing_context_for_variant(product)
25
+ product_price = product.default_variant.price_for(product_pricing_context)
13
26
  %>
14
27
 
15
28
  <div class='<%= sticky_button_classes %> bottom-0 flex flex-col gap-4 z-10' data-sticky-button-target='stickyButton'>
@@ -22,7 +35,7 @@ variant_not_available = selected_variant.nil? ||
22
35
  ].compact.join(' '),
23
36
  product_form_target: 'submit'
24
37
  },
25
- disabled: product.price_in(current_currency).zero? do %>
38
+ disabled: product_price.zero? do %>
26
39
  <% if not_selected_options.size == 1 %>
27
40
  <span><%= Spree.t('storefront.variant_picker.please_choose', option_type: not_selected_options[0].presentation) %></span>
28
41
  <% elsif not_all_options_selected %>
@@ -47,7 +60,7 @@ variant_not_available = selected_variant.nil? ||
47
60
  ].compact.join(' '),
48
61
  product_form_target: 'submit'
49
62
  },
50
- disabled: product.price_in(current_currency).zero? do %>
63
+ disabled: product_price.zero? do %>
51
64
  <% if not_selected_options.size == 1 %>
52
65
  <span><%= Spree.t('storefront.variant_picker.please_choose', option_type: not_selected_options[0].presentation) %></span>
53
66
  <% elsif not_all_options_selected %>
@@ -31,7 +31,7 @@
31
31
  color_preview_container_class: "lg:w-[28px] lg:h-[28px]",
32
32
  color_preview_class: "lg:top-[2px] lg:left-[2px]" %>
33
33
  <% end %>
34
- <% if selected_variant.default_image %>
34
+ <% if selected_variant.has_images? || selected_variant.default_image.present? %>
35
35
  <template data-featured-image-template>
36
36
  <%= render 'spree/products/featured_image',
37
37
  object: selected_variant %>
@@ -1,5 +1,6 @@
1
1
  <% with_cover_image ||= true %>
2
2
  <% object ||= product %>
3
+
3
4
  <% if object.default_image.present? && object.default_image.attached? %>
4
5
  <% height ||= theme_setting('product_listing_image_height') %>
5
6
  <% width ||= theme_setting('product_listing_image_width') %>
@@ -13,7 +14,7 @@
13
14
  "w-full group-hover:opacity-0 transition-opacity duration-500 z-10 relative h-full bg-background product-card !max-h-full #{object_class}" :
14
15
  "w-full h-full !max-h-full #{object_class}" %>
15
16
  <%= spree_image_tag(object.default_image, width: width, height: height, variant: :medium, loading: :lazy, alt: "#{object.name} #{Spree.t('storefront.products.primary_image', default: 'primary image')}", class: image_hover_class) %>
16
- <% if object.secondary_image.present? && object.secondary_image.attached? %>
17
+ <% if object.has_images? && object.image_count > 1 && object.secondary_image.present? && object.secondary_image.attached? %>
17
18
  <% secondary_image_class = "w-full absolute top-0 left-0 opacity-100 h-full !max-h-full #{object_class}" %>
18
19
  <%= spree_image_tag(object.secondary_image, width: width, height: height, variant: :medium, loading: :lazy, alt: "#{object.name} #{Spree.t('storefront.products.secondary_image', default: 'secondary image')}", class: secondary_image_class) %>
19
20
  <% end %>
@@ -5,9 +5,9 @@
5
5
  "@type": "Product",
6
6
  "name": <%= product.name.to_json.html_safe %>,
7
7
  "url": <%= spree.product_url(product, host: current_store.url_or_custom_domain).to_json.html_safe %>,
8
- <% if product.featured_image %>
8
+ <% if product.has_images? %>
9
9
  "image": [
10
- <%= spree_image_url(product.featured_image, variant: :large).to_json.html_safe %>
10
+ <%= spree_image_url(product.default_image, variant: :large).to_json.html_safe %>
11
11
  ],
12
12
  <% end %>
13
13
  <% if product.description.present? %>
@@ -18,10 +18,10 @@
18
18
  <% elsif selected_variant %>
19
19
  <% if selected_variant.sku.present? %>"sku": <%= selected_variant.sku.to_json.html_safe %>,<% end %>
20
20
  <% end %>
21
- <% if product.brand_name %>
21
+ <% if product.brand_taxon %>
22
22
  "brand": {
23
23
  "@type": "Brand",
24
- "name": <%= product.brand_name.to_json.html_safe %>
24
+ "name": <%= product.brand_taxon.name.to_json.html_safe %>
25
25
  },
26
26
  <% end %>
27
27
  "offers": [
@@ -20,7 +20,9 @@
20
20
 
21
21
  target ||= product
22
22
 
23
- price = target.price_in(current_currency)
23
+ variant_for_context = target.is_a?(Spree::Product) ? target.default_variant : target
24
+ pricing_context = pricing_context_for_variant(variant_for_context)
25
+ price = variant_for_context.price_for(pricing_context)
24
26
  money_price = price.display_amount
25
27
 
26
28
  if target.is_a?(Spree::Product) && !use_variant && product.price_varies?(current_currency)
@@ -24,9 +24,9 @@
24
24
  <%= render 'spree/products/label', product: product %>
25
25
  </div>
26
26
  <div class="product-card-inner">
27
- <% if product.brand_name.present? %>
27
+ <% if product.brand_taxon.present? %>
28
28
  <h3 class="text-xs font-semibold uppercase inline-block product-card-brand">
29
- <%= product.brand_name %>
29
+ <%= product.brand_taxon.name %>
30
30
  </h3>
31
31
  <% end %>
32
32
  <h3 class="line-clamp-1 product-card-title">
@@ -55,7 +55,7 @@
55
55
  </div>
56
56
  <% end %>
57
57
  <div class="absolute right-1 top-1 z-10" data-plp-variant-picker-target="addToWishlist">
58
- <%= render 'spree/products/add_to_wishlist', variant: product.first_or_default_variant(current_currency) if product %>
58
+ <%= render 'spree/products/add_to_wishlist', variant: selected_variant %>
59
59
  </div>
60
60
  <%= render 'spree/products/color_swatches', product: product if product && (defined?(show_variant_picker) ? show_variant_picker : true) %>
61
61
  </div>
@@ -1,7 +1,7 @@
1
- <% unless storefront_products.last_page? %>
2
- <%= turbo_frame_tag "next_page", src: url_for(params.to_unsafe_h.merge(page: storefront_products.next_page, format: :turbo_stream)), class: "block relative w-full", data: { controller: "infinite-scroll", infinite_scroll_offset_value: "1350px" }, loading: "lazy" do %>
1
+ <% if storefront_pagy&.next %>
2
+ <%= turbo_frame_tag "next_page", src: url_for(params.to_unsafe_h.merge(page: storefront_pagy.next, format: :turbo_stream)), class: "block relative w-full", data: { controller: "infinite-scroll", infinite_scroll_offset_value: "1350px" }, loading: "lazy" do %>
3
3
  <span class="flex justify-center gap-2 items-center py-4 left-0 w-full h-full">
4
- <%= render 'spree/shared/icons/spinner' %>
4
+ <%= render 'spree/shared/icons/spinner' %>
5
5
  <%= Spree.t(:loading) %>...
6
6
  </span>
7
7
  <% end %>
@@ -0,0 +1,7 @@
1
+ <%# Renders pagination - supports both Pagy and Kaminari %>
2
+ <% if Spree::Storefront::Config[:use_kaminari_pagination] %>
3
+ <%= paginate @posts || @orders || @gift_cards || @store_credit_events || @storefront_products,
4
+ theme: 'storefront', outer_window: 1, inner_window: 2 %>
5
+ <% elsif @pagy && @pagy.pages > 1 %>
6
+ <%== @pagy.series_nav(aria_label: 'Pages') %>
7
+ <% end %>
@@ -6,6 +6,8 @@ module Spree
6
6
  class InstallGenerator < Rails::Generators::Base
7
7
  desc 'Installs Spree Storefront'
8
8
 
9
+ class_option :migrate, type: :boolean, default: true, banner: 'Run migrations'
10
+
9
11
  def self.source_paths
10
12
  [
11
13
  File.expand_path('templates', __dir__),
@@ -14,10 +16,16 @@ module Spree
14
16
  ]
15
17
  end
16
18
 
19
+ def install_page_builder
20
+ say_status :installing, 'page builder'
21
+ migrate_option = options[:migrate] == false ? ' --migrate=false' : ''
22
+ generate "spree:page_builder:install --force#{migrate_option}"
23
+ end
24
+
17
25
  def install
18
26
  empty_directory Rails.root.join('app/assets/tailwind') if Rails.root && !Rails.root.join('app/assets/tailwind').exist?
19
- template 'application.css', 'app/assets/tailwind/application.css'
20
- template 'tailwind.config.js', 'config/tailwind.config.js'
27
+ template 'application.css', 'app/assets/tailwind/application.css', force: options[:force]
28
+ template 'tailwind.config.js', 'config/tailwind.config.js', force: options[:force]
21
29
 
22
30
  if Rails.root && Rails.root.join("Procfile.dev").exist?
23
31
  append_to_file 'Procfile.dev', "\nstorefront_css: bin/rails tailwindcss:watch" unless File.read('Procfile.dev').include?('storefront_css:')
@@ -40,8 +48,8 @@ module Spree
40
48
  append_to_file 'app/assets/config/manifest.js', "\n//= link_tree ../builds" unless File.read('app/assets/config/manifest.js').include?('//= link_tree ../builds')
41
49
  end
42
50
 
43
- # remove static robots.txt as we use robots.txt.erb
44
- remove_file Rails.root.join('public/robots.txt') if Rails.root && Rails.root.join('public/robots.txt').exist?
51
+ # remove static robots.txt as storefront serves it dynamically via seo#robots
52
+ remove_file 'public/robots.txt'
45
53
  end
46
54
  end
47
55
  end
@@ -1850,3 +1850,47 @@ body:has(.currency-and-locale-modal:not(.hidden)) .page-contents {
1850
1850
  aspect-ratio: 1.77;
1851
1851
  }
1852
1852
  }
1853
+
1854
+ /* Pagy pagination styles */
1855
+ .pagy.series-nav {
1856
+ display: flex;
1857
+ align-items: center;
1858
+ justify-content: center;
1859
+ gap: 0.25rem;
1860
+ margin-top: 4rem;
1861
+ }
1862
+
1863
+ .pagy.series-nav a {
1864
+ display: inline-flex;
1865
+ justify-content: center;
1866
+ align-items: center;
1867
+ width: 2rem;
1868
+ text-align: center;
1869
+ padding: 0.25rem;
1870
+ border-radius: 0.25rem;
1871
+ }
1872
+
1873
+ .pagy.series-nav a[rel="prev"],
1874
+ .pagy.series-nav a[rel="next"] {
1875
+ width: auto;
1876
+ padding-left: 1rem;
1877
+ padding-right: 1rem;
1878
+ }
1879
+
1880
+ .pagy.series-nav a[aria-disabled="true"] {
1881
+ opacity: 0.5;
1882
+ cursor: default;
1883
+ }
1884
+
1885
+ .pagy.series-nav a[aria-current="page"] {
1886
+ font-weight: 600;
1887
+ text-decoration: underline;
1888
+ }
1889
+
1890
+ .pagy.series-nav a:not([aria-disabled="true"]):hover {
1891
+ background-color: rgba(0, 0, 0, 0.05);
1892
+ }
1893
+
1894
+ .pagy.series-nav a[role="separator"] {
1895
+ width: 2rem;
1896
+ }
@@ -9,6 +9,9 @@ module Spree
9
9
  preference :products_per_page, :integer, default: 20
10
10
 
11
11
  preference :search_min_query_length, :integer, default: 2
12
+
13
+ # Pagination preference - set to true to use legacy Kaminari pagination
14
+ preference :use_kaminari_pagination, :boolean, default: false
12
15
  end
13
16
  end
14
17
  end
@@ -1,4 +1,5 @@
1
- require 'spree_core'
1
+ require 'spree'
2
+ require 'spree_page_builder'
2
3
 
3
4
  require 'active_link_to'
4
5
  require 'heroicon'
@@ -9,6 +10,7 @@ require 'stimulus-rails'
9
10
  require 'tailwindcss-rails'
10
11
  require 'turbo-rails'
11
12
  require 'inline_svg'
13
+ require 'pagy'
12
14
 
13
15
  require 'spree/storefront/engine'
14
16
  require 'spree/core/partials'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_storefront
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.6
4
+ version: 5.3.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vendo Connect Inc.
@@ -10,19 +10,33 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: spree_core
13
+ name: spree
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 5.2.6
18
+ version: 5.3.0.rc1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 5.2.6
25
+ version: 5.3.0.rc1
26
+ - !ruby/object:Gem::Dependency
27
+ name: spree_page_builder
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.3.0.rc1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 5.3.0.rc1
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: active_link_to
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -163,6 +177,20 @@ dependencies:
163
177
  - - ">="
164
178
  - !ruby/object:Gem::Version
165
179
  version: '0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: pagy
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '43.0'
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '43.0'
166
194
  description: Modern fully featured storefront and checkout for Spree Commerce
167
195
  email: hello@spreecommerce.org
168
196
  executables: []
@@ -177,6 +205,7 @@ files:
177
205
  - app/controllers/concerns/spree/locale_urls.rb
178
206
  - app/controllers/concerns/spree/password_protected.rb
179
207
  - app/controllers/concerns/spree/storefront/devise_concern.rb
208
+ - app/controllers/concerns/spree/storefront/pagination_concern.rb
180
209
  - app/controllers/concerns/spree/theme_concern.rb
181
210
  - app/controllers/spree/account/addresses_controller.rb
182
211
  - app/controllers/spree/account/base_controller.rb
@@ -475,6 +504,7 @@ files:
475
504
  - app/views/themes/default/spree/shared/_order_details.html.erb
476
505
  - app/views/themes/default/spree/shared/_order_line_item.html.erb
477
506
  - app/views/themes/default/spree/shared/_order_shipment.html.erb
507
+ - app/views/themes/default/spree/shared/_pagination.html.erb
478
508
  - app/views/themes/default/spree/shared/_search.html.erb
479
509
  - app/views/themes/default/spree/shared/_title.html.erb
480
510
  - app/views/themes/default/spree/shared/_wishlist_icon.html.erb
@@ -557,9 +587,9 @@ licenses:
557
587
  - AGPL-3.0-or-later
558
588
  metadata:
559
589
  bug_tracker_uri: https://github.com/spree/spree/issues
560
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.2.6
590
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.3.0.rc1
561
591
  documentation_uri: https://docs.spreecommerce.org/
562
- source_code_uri: https://github.com/spree/spree/tree/v5.2.6
592
+ source_code_uri: https://github.com/spree/spree/tree/v5.3.0.rc1
563
593
  rdoc_options: []
564
594
  require_paths:
565
595
  - lib
@@ -567,12 +597,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
567
597
  requirements:
568
598
  - - ">="
569
599
  - !ruby/object:Gem::Version
570
- version: '3.0'
600
+ version: '3.2'
571
601
  required_rubygems_version: !ruby/object:Gem::Requirement
572
602
  requirements:
573
603
  - - ">="
574
604
  - !ruby/object:Gem::Version
575
- version: 1.8.23
605
+ version: '0'
576
606
  requirements: []
577
607
  rubygems_version: 4.0.2
578
608
  specification_version: 4