spree_storefront 5.0.3 → 5.1.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/storefront_page_builder.css +12 -7
  3. data/app/controllers/concerns/spree/locale_urls.rb +13 -1
  4. data/app/controllers/concerns/spree/storefront/devise_concern.rb +42 -0
  5. data/app/controllers/spree/account/wished_items_controller.rb +0 -2
  6. data/app/controllers/spree/addresses_controller.rb +5 -1
  7. data/app/controllers/spree/checkout_controller.rb +27 -9
  8. data/app/controllers/spree/contacts_controller.rb +1 -1
  9. data/app/controllers/spree/products_controller.rb +1 -3
  10. data/app/controllers/spree/store_controller.rb +16 -9
  11. data/app/controllers/spree/taxons_controller.rb +1 -3
  12. data/app/helpers/spree/analytics_helper.rb +6 -2
  13. data/app/helpers/spree/checkout_analytics_helper.rb +1 -1
  14. data/app/helpers/spree/checkout_helper.rb +1 -1
  15. data/app/helpers/spree/filters_helper.rb +1 -1
  16. data/app/helpers/spree/fonts_helper.rb +3 -1
  17. data/app/helpers/spree/page_helper.rb +15 -0
  18. data/app/helpers/spree/products_helper.rb +13 -1
  19. data/app/helpers/spree/storefront_helper.rb +12 -0
  20. data/app/helpers/spree/storefront_locale_helper.rb +3 -0
  21. data/app/helpers/spree/theme_helper.rb +76 -0
  22. data/app/helpers/spree/wishlist_helper.rb +3 -0
  23. data/app/javascript/spree/storefront/controllers/product_form_controller.js +21 -18
  24. data/app/javascript/spree/storefront/controllers/quantity_picker_controller.js +16 -9
  25. data/app/javascript/spree/storefront/controllers/slideover_controller.js +2 -0
  26. data/app/javascript/spree/storefront/controllers/wished_item_controller.js +4 -2
  27. data/app/views/devise/registrations/_form.html.erb +2 -2
  28. data/app/views/devise/sessions/new.html.erb +16 -0
  29. data/app/views/layouts/spree/checkout.html.erb +1 -1
  30. data/app/views/layouts/spree/storefront.html.erb +1 -1
  31. data/app/views/spree/account/wished_items/create.turbo_stream.erb +1 -0
  32. data/app/views/spree/addresses/edit.html.erb +4 -0
  33. data/app/views/spree/addresses/new.html.erb +3 -0
  34. data/app/views/spree/checkout/edit.html.erb +5 -3
  35. data/app/views/spree/line_items/create.turbo_stream.erb +1 -0
  36. data/app/views/spree/line_items/destroy.turbo_stream.erb +2 -0
  37. data/app/views/spree/products/show.html.erb +2 -1
  38. data/app/views/themes/default/spree/account/addresses/_address.html.erb +14 -4
  39. data/app/views/themes/default/spree/account/addresses/_edit_address_modal.html.erb +38 -0
  40. data/app/views/themes/default/spree/account/addresses/_new_address_modal.html.erb +38 -0
  41. data/app/views/themes/default/spree/account/addresses/index.html.erb +22 -2
  42. data/app/views/themes/default/spree/orders/_line_item_quantity.html.erb +1 -1
  43. data/app/views/themes/default/spree/orders/edit.html.erb +2 -0
  44. data/app/views/themes/default/spree/page_sections/_newsletter.html.erb +1 -1
  45. data/app/views/themes/default/spree/page_sections/_product_grid.html.erb +16 -9
  46. data/app/views/themes/default/spree/products/_add_to_cart_button.html.erb +8 -2
  47. data/app/views/themes/default/spree/products/_add_to_wishlist.html.erb +2 -0
  48. data/app/views/themes/default/spree/products/filters/_taxons.erb +5 -5
  49. data/app/views/themes/default/spree/shared/_error_messages.html.erb +1 -1
  50. data/lib/generators/spree/storefront/devise/devise_generator.rb +47 -0
  51. data/lib/generators/spree/storefront/devise/templates/user_passwords_controller.rb +17 -0
  52. data/lib/generators/spree/storefront/devise/templates/user_registrations_controller.rb +17 -0
  53. data/lib/generators/spree/storefront/devise/templates/user_sessions_controller.rb +17 -0
  54. data/lib/spree/storefront/engine.rb +7 -1
  55. metadata +13 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a3165bdc79f4acc7b7ac36a86572a2f6d114fdacc1b125af2b0e86d21983d92
4
- data.tar.gz: 852b2926ade744ce9b0c41f0d5f04d68b10c3d5408100b3531dea0966b663fee
3
+ metadata.gz: c8bc38aa5cb40c780c3a1fa2f20d1672b4f95986c66261a1833f725a417fb15b
4
+ data.tar.gz: 4a04f0367d6c4fd8b53a56a1eb3e7f635c4af6c116751367952e4a6153544c4a
5
5
  SHA512:
6
- metadata.gz: 2727b7cca478cbe94b87e22cca326b11259138f2e36069487d546bb865cc4ca6952b05e94595350e31907916acb2c1d6f1a22ba45e3dbf382cf3c3af5c000aad
7
- data.tar.gz: 5bfc483ae97000fe62611cb3cd7fd39eca7c28104e8a193016a5e21dcf35ec2ee526c8fd1311cd2a4de42dfa95cafec65a5d791ceec662d586b92774d35daa48
6
+ metadata.gz: befdf20cbbb63756d6f026ffe47502daa56ffbd3db39048a98c3b9a2d1303e2f08475036004e3bd61f123fce1f05ce53d3d710d520a1a569b2daeb263d573ecd
7
+ data.tar.gz: c78e23552f955bbe171d2db49d883027002a2f7eb0b5532689819ae64b6595f05dcc4df76e0a95e3e88cf1f93e6497cc26666ac8c5a9b3c333cb414fe8983a8f
@@ -1,22 +1,18 @@
1
1
  .editor-overlay {
2
2
  z-index: 1089;
3
3
  width: 100%;
4
+ container-type: inline-size;
5
+ container-name: editor-overlay;
4
6
  }
5
7
 
6
8
  .editor-overlay-toolbar {
7
9
  position: absolute;
8
- top: -26px;
9
- left: -1px;
10
+ min-width: max-content;
10
11
  background-color: #0081f1;
11
12
  color: #fff;
12
- padding: 4px 8px;
13
13
  font-size: 12px;
14
14
  font-weight: bold;
15
- border-radius: 4px 4px 0 0;
16
- cursor: pointer;
17
15
  display: none;
18
- }
19
- .editor-overlay-toolbar {
20
16
  right: 2px;
21
17
  top: 2px;
22
18
  left: auto;
@@ -25,6 +21,15 @@
25
21
  padding: 2px 2px;
26
22
  border-radius: 4px;
27
23
  }
24
+
25
+ /* If the editor overlay is really small, move the toolbar to the bottom so it doesn't cover the content */
26
+ @container editor-overlay (max-width: 200px) {
27
+ .editor-overlay-toolbar {
28
+ top: 100%;
29
+ right: 0px;
30
+ }
31
+ }
32
+
28
33
  .editor-overlay-toolbar button {
29
34
  padding: 8px;
30
35
  border-radius: 4px;
@@ -21,7 +21,19 @@ module Spree
21
21
  def redirect_to_default_locale
22
22
  return if params[:locale].blank? || supported_locale?(params[:locale])
23
23
 
24
- redirect_to url_for(request.parameters.merge(locale: nil))
24
+ # Only include safe parameters in the redirect
25
+ safe_params = {
26
+ controller: params[:controller],
27
+ action: params[:action],
28
+ locale: nil
29
+ }
30
+
31
+ # Add any additional safe parameters that should be preserved
32
+ %i[id format currency category_id tag].each do |param|
33
+ safe_params[param] = params[param] if params[param].present?
34
+ end
35
+
36
+ redirect_to url_for(safe_params)
25
37
  end
26
38
  end
27
39
  end
@@ -0,0 +1,42 @@
1
+ # This concern is used to include the necessary methods and helpers for the Devise controllers
2
+ # It is used to avoid repeating the same code in each controller
3
+ module Spree
4
+ module Storefront
5
+ module DeviseConcern
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ helper_method :title
10
+ helper_method :stored_location
11
+ layout 'spree/storefront'
12
+
13
+ include Spree::Core::ControllerHelpers::Order
14
+ include Spree::LocaleUrls
15
+ include Spree::ThemeConcern
16
+ include Spree::IntegrationsHelper if defined?(Spree::IntegrationsHelper)
17
+
18
+ helper 'spree/wishlist'
19
+ helper 'spree/currency'
20
+ helper 'spree/locale'
21
+ helper 'spree/storefront_locale'
22
+ helper 'spree/integrations' if defined?(Spree::IntegrationsHelper)
23
+ end
24
+
25
+ def stored_location
26
+ return unless defined?(after_sign_in_path_for)
27
+ return unless defined?(store_location_for)
28
+ return unless defined?(Devise)
29
+
30
+ path = after_sign_in_path_for(Devise.mappings.keys.first)
31
+
32
+ store_location_for(Devise.mappings.keys.first, path)
33
+
34
+ path
35
+ end
36
+
37
+ def password_path(_resource_or_scope = nil)
38
+ send("#{Spree.user_class.model_name.singular_route_key}_password_path")
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,8 +1,6 @@
1
1
  module Spree
2
2
  module Account
3
3
  class WishedItemsController < BaseController
4
- skip_before_action :verify_authenticity_token
5
-
6
4
  # POST /account/wished_items
7
5
  def create
8
6
  variant = current_store.variants.find(wished_item_params[:variant_id])
@@ -26,6 +26,8 @@ module Spree
26
26
  else
27
27
  redirect_to spree.account_addresses_path, notice: Spree.t('address_book.successfully_created')
28
28
  end
29
+ elsif params[:from_modal].present?
30
+ render turbo_stream: turbo_stream.update(:new_address_modal, partial: 'spree/account/addresses/new_address_modal', locals: { address: @address }), status: :unprocessable_entity
29
31
  else
30
32
  render action: 'new', status: :unprocessable_entity
31
33
  end
@@ -67,8 +69,10 @@ module Spree
67
69
  format.html { redirect_to spree.checkout_state_path(@order.token, 'address') }
68
70
  end
69
71
  else
70
- redirect_back_or_default(spree.account_addresses_path)
72
+ redirect_back(fallback_location: spree.account_addresses_path)
71
73
  end
74
+ elsif params[:from_modal].present?
75
+ render turbo_stream: turbo_stream.update("edit_address_modal_#{@address.id}", partial: 'spree/account/addresses/edit_address_modal', locals: { address: @address }), status: :unprocessable_entity
72
76
  else
73
77
  render :edit, status: :unprocessable_entity
74
78
  end
@@ -14,6 +14,7 @@ module Spree
14
14
 
15
15
  before_action :ensure_order_not_completed, only: [:edit, :update]
16
16
  before_action :ensure_checkout_allowed
17
+ before_action :check_if_checkout_started, only: :edit
17
18
  before_action :ensure_valid_state, only: [:edit, :update]
18
19
 
19
20
  before_action :restart_checkout, only: :edit, if: :should_restart_checkout?
@@ -33,13 +34,6 @@ module Spree
33
34
 
34
35
  # GET /checkout/<token>
35
36
  def edit
36
- if checkout_started?
37
- track_checkout_started
38
-
39
- @order.accept_marketing = true # TODO: move this to store preferences
40
- @order.signup_for_an_account = true # TODO: move this to store preferences
41
- end
42
-
43
37
  track_checkout_step_viewed
44
38
  end
45
39
 
@@ -179,8 +173,23 @@ module Spree
179
173
  flash[:error] = 'You cannot access this checkout'
180
174
  redirect_to_cart
181
175
  elsif try_spree_current_user.nil? && !@order.completed?
182
- store_location
183
- redirect_to spree_login_path
176
+ if params[:guest] && current_store.prefers_guest_checkout?
177
+ @order = current_store.
178
+ orders.
179
+ create!(current_order_params.except(:token, :user_id)).
180
+ tap do |order|
181
+ order.merge!(@order, discard_merged: false)
182
+ order.disassociate_user!
183
+ end
184
+
185
+ reset_session
186
+ cookies.permanent.signed[:token] = @order.token
187
+
188
+ redirect_to spree.checkout_path(@order.token)
189
+ else
190
+ store_location
191
+ redirect_to spree_login_path
192
+ end
184
193
  end
185
194
  end
186
195
 
@@ -227,6 +236,15 @@ module Spree
227
236
  end
228
237
  end
229
238
 
239
+ def check_if_checkout_started
240
+ if checkout_started?
241
+ track_checkout_started
242
+
243
+ @order.accept_marketing = true # TODO: move this to store preferences
244
+ @order.signup_for_an_account = true # TODO: move this to store preferences
245
+ end
246
+ end
247
+
230
248
  def ensure_valid_state
231
249
  redirect_to_state(correct_state) if @order.state != correct_state && !skip_state_validation?
232
250
  end
@@ -40,7 +40,7 @@ module Spree
40
40
  return if current_store.customer_support_email.present?
41
41
 
42
42
  flash[:error] = Spree.t('storefront.contacts.customer_support_email_not_configured')
43
- redirect_back_or_default root_path
43
+ redirect_back(fallback_location: root_path)
44
44
  end
45
45
  end
46
46
  end
@@ -77,9 +77,7 @@ module Spree
77
77
  # If an old id or a numeric id was used to find the record,
78
78
  # we should do a 301 redirect that uses the current friendly id.
79
79
  if params[:id] != @product.friendly_id
80
- params[:id] = @product.friendly_id
81
- params.permit!
82
- redirect_to url_for(params), status: :moved_permanently
80
+ redirect_to spree.product_path(@product), status: :moved_permanently
83
81
  end
84
82
  end
85
83
 
@@ -8,6 +8,7 @@ module Spree
8
8
  include Spree::PasswordProtected
9
9
  include Spree::WishlistHelper
10
10
  include Spree::AnalyticsHelper
11
+ include Spree::IntegrationsHelper
11
12
 
12
13
  layout :choose_layout
13
14
 
@@ -17,6 +18,7 @@ module Spree
17
18
  helper 'spree/currency'
18
19
  helper 'spree/addresses'
19
20
  helper 'spree/wishlist'
21
+ helper 'spree/integrations'
20
22
 
21
23
  helper_method :title
22
24
  helper_method :title=
@@ -28,6 +30,8 @@ module Spree
28
30
  :storefront_products_scope, :storefront_products,
29
31
  :default_products_sort, :default_products_finder_params
30
32
 
33
+ helper_method :stored_location
34
+
31
35
  before_action :redirect_to_default_locale
32
36
  before_action :render_404_if_store_not_exists
33
37
  rescue_from ActionController::InvalidAuthenticityToken, with: :invalid_authenticity_token
@@ -69,7 +73,10 @@ module Spree
69
73
  :out_of_stock,
70
74
  {
71
75
  options: [store_filter_names_hash],
72
- taxon_ids: []
76
+ taxon_ids: [],
77
+ taxonomy_ids: [
78
+ taxon_ids: []
79
+ ]
73
80
  }
74
81
  ]
75
82
  )
@@ -191,7 +198,8 @@ module Spree
191
198
  end
192
199
 
193
200
  def redirect_back_or_default(default)
194
- redirect_to(session[:user_return_to] || default)
201
+ 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.')
202
+ redirect_back(fallback_location: default)
195
203
  end
196
204
 
197
205
  def require_user(return_to = nil)
@@ -205,16 +213,15 @@ module Spree
205
213
  end
206
214
  end
207
215
 
208
- # this will work for devise out of the box
209
- # for other auth systems you will need to override this method
210
- def store_location(location = nil)
211
- return if try_spree_current_user
212
- return unless defined?(store_location_for)
216
+ def stored_location
217
+ return unless defined?(after_sign_in_path_for)
213
218
  return unless defined?(Devise)
214
219
 
215
- location ||= request.fullpath
220
+ path = after_sign_in_path_for(Devise.mappings.keys.first)
221
+
222
+ store_location(path)
216
223
 
217
- store_location_for(Devise.mappings.keys.first, location)
224
+ path
218
225
  end
219
226
 
220
227
  def redirect_to_cart
@@ -33,9 +33,7 @@ module Spree
33
33
  # If an old id or a numeric id was used to find the record,
34
34
  # we should do a 301 redirect that uses the current friendly id.
35
35
  if params[:id] != @taxon.friendly_id
36
- params[:id] = @taxon.friendly_id
37
- params.permit!
38
- redirect_to url_for(params), status: :moved_permanently
36
+ redirect_to spree.nested_taxons_path(@taxon), status: :moved_permanently
39
37
  end
40
38
  end
41
39
 
@@ -2,7 +2,7 @@ module Spree
2
2
  module AnalyticsHelper
3
3
  def analytics_event_handlers
4
4
  @analytics_event_handlers ||= Spree::Analytics.event_handlers.map do |handler|
5
- handler.new(user: try_spree_current_user, session: session, request: request)
5
+ handler.new(user: try_spree_current_user, session: session, request: request, store: Spree::Store.current, visitor_id: visitor_id)
6
6
  end
7
7
  end
8
8
 
@@ -16,11 +16,15 @@ module Spree
16
16
  rescue => e
17
17
  Rails.error.report(
18
18
  e,
19
- context: { event_name: event_name, record_id: record&.id, record_type: record&.class&.name },
19
+ context: { event_name: event_name, record: record },
20
20
  source: 'spree.storefront'
21
21
  )
22
22
  end
23
23
 
24
+ def visitor_id
25
+ session[:spree_visitor_token] ||= SecureRandom.uuid
26
+ end
27
+
24
28
  def unsupported_event?(event_name)
25
29
  !Spree::Analytics.events.key?(event_name.to_sym)
26
30
  end
@@ -68,7 +68,7 @@ module Spree
68
68
 
69
69
  def track_checkout_completed
70
70
  # server-side tracking
71
- track_event('checkout_completed', { order: @order })
71
+ track_event('order_completed', { order: @order })
72
72
 
73
73
  # client-side tracking
74
74
  session[:checkout_completed] = true
@@ -33,7 +33,7 @@ module Spree
33
33
  end
34
34
 
35
35
  def checkout_started?
36
- @order.state == 'address' && @order.state_was == 'cart'
36
+ (@order.state == 'address' && @order.state_was == 'cart') || (@order.state == 'cart' && correct_state == 'address')
37
37
  end
38
38
 
39
39
  def already_have_an_account?
@@ -92,7 +92,7 @@ module Spree
92
92
  end
93
93
 
94
94
  def storefront_products_for_taxon_filters
95
- @storefront_products_for_taxon_filters ||= products_for_filters_scope(except_filters: [:taxons])
95
+ @storefront_products_for_taxon_filters ||= products_for_filters_scope(except_filters: [:taxons, :taxonomy_ids])
96
96
  end
97
97
 
98
98
  def filter_taxon_ids
@@ -38,8 +38,10 @@ module Spree
38
38
 
39
39
  return if fonts.blank?
40
40
 
41
+ font_weights = (200..700).step(100).to_a
42
+
41
43
  imports = fonts.map do |font|
42
- "family=#{font.split(' ').join('+')}:wght@200..700"
44
+ "family=#{font.split.join('+')}:wght@#{font_weights.join(';')}"
43
45
  end.compact.join('&')
44
46
 
45
47
  return if imports.blank?
@@ -1,5 +1,13 @@
1
1
  module Spree
2
2
  module PageHelper
3
+ # Renders the page with the current theme (or theme preview) and page preview if it exists.
4
+ # It fetches all page sections and renders them one by one in the order they are set in the page builder by store staff.
5
+ # It also handles lazy loading of sections.
6
+ #
7
+ # @param page [Spree::Page] the page to render
8
+ # @param variables [Hash] variables to pass to the page sections
9
+ # @option variables [Array] :pickup_locations ([]) the pickup locations to pass to the page sections
10
+ # @return [String] the rendered page
3
11
  def render_page(page = nil, variables = {})
4
12
  page ||= current_page
5
13
 
@@ -11,6 +19,12 @@ module Spree
11
19
  "<main class='page-contents'>#{sections_html}</main>".html_safe
12
20
  end
13
21
 
22
+ # Renders a single section of the page.
23
+ #
24
+ # @param section [Spree::PageSection] the section to render
25
+ # @param variables [Hash] variables to pass to the section
26
+ # @option variables [Boolean] :lazy_allowed (true) whether lazy loading is allowed for the section (if it supports it)
27
+ # @return [String] the rendered section
14
28
  def render_section(section, variables = {}, lazy_allowed: true)
15
29
  return '' if section.blank?
16
30
 
@@ -54,6 +68,7 @@ module Spree
54
68
  ''
55
69
  end
56
70
 
71
+ # Renders a link to the page builder for the given link.
57
72
  def page_builder_link_to(link, options = {}, &block)
58
73
  if link.present?
59
74
  link_to(spree_storefront_resource_url(link.linkable || link), options.except(:label)) do
@@ -21,11 +21,15 @@ module Spree
21
21
  end
22
22
 
23
23
  def weeks_online(product)
24
+ Spree::Deprecation.warn('weeks_online is deprecated and will be removed in Spree 5.2')
25
+
24
26
  (Time.current - product.activated_at.in_time_zone(current_store.preferred_timezone)).seconds.in_weeks.to_i.abs
25
27
  end
26
28
 
27
29
  def brand_name(product)
28
- product.brand&.name || product.vendor&.display_name
30
+ Spree::Deprecation.warn('brand_name is deprecated and will be removed in Spree 5.2. Please use `product.brand_name` instead.')
31
+
32
+ product.brand&.name || product.try(:vendor)&.display_name
29
33
  end
30
34
 
31
35
  def product_not_selected_options(product, selected_variant, options_param_name: :options)
@@ -152,6 +156,10 @@ module Spree
152
156
  product.main_taxon.self_and_ancestors.find_all { |taxon| taxon.depth != 0 }
153
157
  end
154
158
 
159
+ # Generates the JSON-LD elements for a list of products.
160
+ #
161
+ # @param product_slugs [Array<String>] The slugs of the products to generate elements for
162
+ # @return [Array<Hash>] The JSON-LD elements
155
163
  def product_list_json_ld_elements(product_slugs)
156
164
  product_slugs.each_with_index.map do |product_slug, index|
157
165
  {
@@ -162,6 +170,10 @@ module Spree
162
170
  end
163
171
  end
164
172
 
173
+ # Generates the JSON-LD breadcrumbs for a product.
174
+ #
175
+ # @param product [Spree::Product] The product to generate breadcrumbs for
176
+ # @return [Hash] The JSON-LD breadcrumbs
165
177
  def product_json_ld_breadcrumbs(product)
166
178
  json_ld = {
167
179
  '@context' => 'https://schema.org',
@@ -3,12 +3,20 @@ module Spree
3
3
  include BaseHelper
4
4
  include Heroicon::Engine.helpers
5
5
 
6
+ # Renders the storefront partials for the given section.
7
+ #
8
+ # @param section [String] The section to render
9
+ # @param options [Hash] The options/variables to pass to the partials
10
+ # @return [String] The rendered partials
6
11
  def render_storefront_partials(section, options = {})
7
12
  Rails.application.config.spree_storefront.send(section).map do |partial|
8
13
  render partial, options
9
14
  end.join.html_safe
10
15
  end
11
16
 
17
+ # Returns the page description for the current page.
18
+ #
19
+ # @return [String] The page description
12
20
  def page_description
13
21
  return @page_description if @page_description.present?
14
22
 
@@ -23,6 +31,10 @@ module Spree
23
31
  @page_description
24
32
  end
25
33
 
34
+ # Returns the page image for the current page.
35
+ # This is used for SEO, social media and Open Graph tags.
36
+ #
37
+ # @return [String] The page image
26
38
  def page_image
27
39
  return @page_image if @page_image.present?
28
40
 
@@ -1,5 +1,8 @@
1
1
  module Spree
2
2
  module StorefrontLocaleHelper
3
+ # Returns the currently selected locale.
4
+ #
5
+ # @return [String] The currently selected locale
3
6
  def current_locale
4
7
  @current_locale ||= if user_locale?
5
8
  try_spree_current_user.selected_locale