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
@@ -1,9 +1,16 @@
1
1
  module Spree
2
2
  module ThemeHelper
3
+ # Returns the current page, if not found it will fallback to the homepage
4
+ #
5
+ # @return [Spree::Page] the current page
3
6
  def current_page
4
7
  @current_page ||= current_theme.pages.find_by(type: 'Spree::Pages::Homepage')
5
8
  end
6
9
 
10
+ # Returns the current theme, if not found it will fallback to the default theme
11
+ # If `theme_id` is provided in the params, it will return the theme with the given id
12
+ #
13
+ # @return [Spree::Theme] the current theme
7
14
  def current_theme
8
15
  @current_theme ||= if params[:theme_id].present?
9
16
  current_store.themes.find_by(id: params[:theme_id])
@@ -14,34 +21,56 @@ module Spree
14
21
  @current_theme ||= current_store.themes.first
15
22
  end
16
23
 
24
+ # Returns the current theme preview
25
+ #
26
+ # @return [Spree::ThemePreview] the current theme preview
17
27
  def current_theme_preview
18
28
  return if params[:theme_preview_id].blank?
19
29
 
20
30
  @current_theme_preview ||= current_theme.previews.find_by(id: params[:theme_preview_id])
21
31
  end
22
32
 
33
+ # Returns the current page preview
34
+ #
35
+ # @return [Spree::PagePreview] the current page preview
23
36
  def current_page_preview
24
37
  return if params[:page_preview_id].blank?
25
38
 
26
39
  @current_page_preview ||= current_page.previews.find_by(id: params[:page_preview_id])
27
40
  end
28
41
 
42
+ # Returns the current page or page preview, preview takes priority
43
+ #
44
+ # @return [Spree::Page] the current page or page preview
29
45
  def current_page_or_preview
30
46
  @current_page_or_preview ||= current_page_preview || current_page
31
47
  end
32
48
 
49
+ # Returns the current theme or theme preview, preview takes priority
50
+ #
51
+ # @return [Spree::Theme] the current theme or theme preview
33
52
  def current_theme_or_preview
34
53
  @current_theme_or_preview ||= current_theme_preview || current_theme
35
54
  end
36
55
 
56
+ # Returns the logo set in the `Spree::PageSections::Header` section
57
+ #
58
+ # @return [ActiveStorage::Attachment] the logo
37
59
  def current_header_logo
38
60
  @current_header_logo ||= current_theme_or_preview.sections.find_by(type: 'Spree::PageSections::Header')&.logo
39
61
  end
40
62
 
63
+ # Returns whether the page builder is enabled
64
+ # It checks if there is a theme preview or page preview and if the `page_builder` param is set to `true`
65
+ #
66
+ # @return [Boolean] whether the page builder is enabled
41
67
  def page_builder_enabled?
42
68
  @page_builder_enabled ||= (current_theme_preview.present? || current_page_preview.present?) && params[:page_builder] == 'true'
43
69
  end
44
70
 
71
+ # Returns the theme layout sections, eg. header, footer, etc.
72
+ #
73
+ # @return [Hash] the theme layout sections
45
74
  def theme_layout_sections
46
75
  @theme_layout_sections ||= current_theme_or_preview.sections.includes(:links, { asset_attachment: :blob },
47
76
  { blocks: [:rich_text_text, :links] }).all.each_with_object({}) do |section, hash|
@@ -55,6 +84,11 @@ module Spree
55
84
  {}
56
85
  end
57
86
 
87
+ # Returns the theme setting for the given name
88
+ # if preview is present, it will return the preview setting, otherwise it will return the theme setting
89
+ #
90
+ # @param name [String] the name of the theme setting
91
+ # @return [String] the theme setting
58
92
  def theme_setting(name)
59
93
  if current_theme_preview.present?
60
94
  current_theme_preview.preferences.with_indifferent_access[name]
@@ -64,6 +98,9 @@ module Spree
64
98
  end
65
99
 
66
100
  # This helper allows us to specify opacity in Tailwind's color palette
101
+ #
102
+ # @param name [String] the name of the theme setting
103
+ # @return [String] the theme setting
67
104
  def theme_setting_rgb_components(name)
68
105
  hex_color = theme_setting(name)
69
106
  return unless hex_color.present?
@@ -73,6 +110,10 @@ module Spree
73
110
  end
74
111
 
75
112
  # https://makandracards.com/makandra/496431-ruby-how-to-convert-hex-color-codes-to-rgb-or-rgba
113
+ # Converts a hex color to rgb
114
+ #
115
+ # @param hex [String] the hex color
116
+ # @return [String] the rgb color
76
117
  def hex_color_to_rgb(hex)
77
118
  return unless hex.present?
78
119
 
@@ -80,6 +121,10 @@ module Spree
80
121
  "rgb(#{rgb.join(', ')})"
81
122
  end
82
123
 
124
+ # Converts a hex color to rgba
125
+ #
126
+ # @param hex [String] the hex color
127
+ # @return [String] the rgba color
83
128
  def hex_color_to_rgba(hex)
84
129
  return unless hex.present?
85
130
 
@@ -88,6 +133,10 @@ module Spree
88
133
  "rgba(#{rgb.join(', ')}, #{opacity.round(2)})"
89
134
  end
90
135
 
136
+ # Returns the section inline CSS styles
137
+ #
138
+ # @param section [Spree::PageSection] the section
139
+ # @return [String] the section inline CSS styles
91
140
  def section_styles(section)
92
141
  styles = {}
93
142
 
@@ -116,6 +165,10 @@ module Spree
116
165
  styles.map { |k, v| "#{k}: #{v}" }.join(';')
117
166
  end
118
167
 
168
+ # Returns the section heading inline CSS styles
169
+ #
170
+ # @param section [Spree::PageSection] the section
171
+ # @return [String] the section heading inline CSS styles
119
172
  def section_heading_styles(section)
120
173
  styles = {}
121
174
 
@@ -128,6 +181,11 @@ module Spree
128
181
  styles.compact_blank.map { |k, v| "#{k}: #{v}" }.join(';')
129
182
  end
130
183
 
184
+ # Returns the block HTML attributes
185
+ # it automatically adds data attributes for page builder
186
+ #
187
+ # @param block [Spree::PageBlock] the block
188
+ # @return [Hash] the block attributes
131
189
  def block_attributes(block, allowed_styles: :all)
132
190
  has_width_desktop = block.respond_to?(:preferred_width_desktop) && block.preferred_width_desktop.present? ? "width-desktop='true'" : nil
133
191
 
@@ -147,6 +205,11 @@ module Spree
147
205
  tag.attributes(attributes)
148
206
  end
149
207
 
208
+ # Returns the link HTML attributes
209
+ # it automatically adds data attributes for page builder
210
+ #
211
+ # @param link [Spree::PageLink] the link
212
+ # @return [Hash] the link attributes
150
213
  def link_attributes(link, as_html: true)
151
214
  parent_type = case link.parent_type
152
215
  when 'Spree::PageSection'
@@ -184,6 +247,11 @@ module Spree
184
247
  end
185
248
  end
186
249
 
250
+ # Returns the block inline CSS styles
251
+ #
252
+ # @param block [Spree::PageBlock] the block
253
+ # @param allowed_styles [Symbol] the allowed styles, if not provided, all styles will be returned
254
+ # @return [String] the block inline CSS styles
187
255
  def block_styles(block, allowed_styles: :all)
188
256
  styles = {}
189
257
 
@@ -229,12 +297,20 @@ module Spree
229
297
  styles.map { |k, v| "#{k}: #{v}" }.join(';')
230
298
  end
231
299
 
300
+ # Returns the block background color style
301
+ #
302
+ # @param block [Spree::PageBlock] the block
303
+ # @return [String] the block background color style
232
304
  def block_background_color_style(block)
233
305
  return nil unless block.respond_to?(:preferred_background_color) && block.preferred_background_color.present?
234
306
 
235
307
  "background-color: #{block.preferred_background_color};"
236
308
  end
237
309
 
310
+ # Returns the block CSS classes
311
+ #
312
+ # @param block [Spree::PageBlock] the block
313
+ # @return [String] the block CSS classes
238
314
  def block_css_classes(block)
239
315
  classes = []
240
316
  classes << "justify-#{block.preferred_justify}" if block.respond_to?(:preferred_justify) && block.preferred_justify.present?
@@ -1,5 +1,8 @@
1
1
  module Spree
2
2
  module WishlistHelper
3
+ # Returns the current wishlist for the current user.
4
+ #
5
+ # @return [Spree::Wishlist] The current wishlist
3
6
  def current_wishlist
4
7
  @current_wishlist ||= try_spree_current_user&.default_wishlist_for_store(current_store)
5
8
  end
@@ -28,20 +28,12 @@ export default class extends Controller {
28
28
  }
29
29
 
30
30
  connect() {
31
- const selectedOptions = new Set()
32
- this.optionTargets.forEach((option) => {
33
- if (option.checked) {
34
- selectedOptions.add(option.dataset.optionId)
35
- }
36
- })
37
- const requiredOptions = new Set(this.requiredOptionsValue)
31
+ if (this.hasAddToWishlistTarget && this.variantFromOptionsDisabledValue) {
32
+ const notSelectedOptions = this.getNotSelectedOptions()
38
33
 
39
- this.notSelectedOptions = [...requiredOptions].filter((x) => !selectedOptions.has(x))
40
- if (this.notSelectedOptions.length) {
41
- this.submitTargets.forEach((button) => button.addEventListener('click', this.showNotSelectedOptions))
42
- }
43
- if (this.hasAddToWishlistTarget && this.variantFromOptionsDisabledValue && this.notSelectedOptions.length === 0) {
44
- this.addToWishlistTarget.disabled = true
34
+ if (notSelectedOptions.length === 0) {
35
+ this.addToWishlistTarget.disabled = true
36
+ }
45
37
  }
46
38
 
47
39
  if (this.hasDesktopMediaGalleryTarget && this.hasProductDetailsTarget) {
@@ -56,14 +48,14 @@ export default class extends Controller {
56
48
  this.submitTargets.forEach((button) => button.addEventListener('turbo-stream-form:submit-end', this.enableForm))
57
49
  }
58
50
 
59
- disconnect() {
60
- this.submitTargets.forEach((button) => button.removeEventListener('click', this.showNotSelectedOptions))
61
- }
62
-
63
51
  showNotSelectedOptions = (e) => {
52
+ const notSelectedOptions = this.getNotSelectedOptions()
53
+
54
+ if (!notSelectedOptions.length) return
55
+
64
56
  e.preventDefault()
65
57
 
66
- this.notSelectedOptions.forEach((option, index) => {
58
+ notSelectedOptions.forEach((option, index) => {
67
59
  const fieldSetElement = this.element.querySelector(`[data-option-id="${option}"]`)
68
60
  if (index === 0) {
69
61
  const toggleElement = fieldSetElement.querySelector('[data-controller="dropdown"]')
@@ -133,4 +125,15 @@ export default class extends Controller {
133
125
  enableForm = () => {
134
126
  this.disabledValue = false
135
127
  }
128
+
129
+ getNotSelectedOptions() {
130
+ const selectedOptions = new Set()
131
+ this.optionTargets.forEach((option) => {
132
+ if (option.checked) {
133
+ selectedOptions.add(option.dataset.optionId)
134
+ }
135
+ })
136
+ const requiredOptions = new Set(this.requiredOptionsValue)
137
+ return [...requiredOptions].filter((x) => !selectedOptions.has(x))
138
+ }
136
139
  }
@@ -3,41 +3,48 @@ import { Controller } from "@hotwired/stimulus"
3
3
  export default class extends Controller {
4
4
  static targets = [ 'quantity', 'increase', 'decrease' ]
5
5
 
6
+ static values = {
7
+ min: { type: Number, default: 1 },
8
+ max: { type: Number, default: 9999 }
9
+ }
10
+
11
+ static classes = ['disabled']
12
+
6
13
  connect() {
7
- if (this.quantity <= 1) this.disableButton(this.decreaseTarget)
14
+ if (this.quantity <= this.minValue) this.disableButton(this.decreaseTarget)
8
15
  }
9
16
 
10
17
  get quantity() {
11
- return parseInt(this.quantityTarget.value) || 1
18
+ return parseInt(this.quantityTarget.value) || this.minValue
12
19
  }
13
20
 
14
21
  get maxQuantity() {
15
- return parseInt(this.quantityTarget.max) || 9999
22
+ return parseInt(this.quantityTarget.max) || this.maxValue
16
23
  }
17
24
 
18
25
  set quantity(value) {
19
- this.quantityTarget.value = parseInt(value) || 1
26
+ this.quantityTarget.value = parseInt(value) || this.minValue
20
27
  }
21
28
 
22
29
  increase() {
23
30
  if (this.quantity < this.maxQuantity) this.quantity = this.quantity + 1
24
- if (this.quantity > 1) this.enableButton(this.decreaseTarget)
31
+ if (this.quantity > this.minValue) this.enableButton(this.decreaseTarget)
25
32
  if (this.quantity == this.maxQuantity && this.increaseTarget.type != 'submit') this.disableButton(this.increaseTarget)
26
33
  }
27
34
 
28
35
  decrease() {
29
- if (this.quantity > 1) this.quantity = this.quantity - 1
30
- if (this.quantity == 1 && this.decreaseTarget.type != 'submit') this.disableButton(this.decreaseTarget)
36
+ if (this.quantity > this.minValue) this.quantity = this.quantity - 1
37
+ if (this.quantity == this.minValue && this.decreaseTarget.type != 'submit') this.disableButton(this.decreaseTarget)
31
38
  if (this.quantity < this.maxQuantity) this.enableButton(this.increaseTarget)
32
39
  }
33
40
 
34
41
  disableButton(button) {
35
42
  button.setAttribute('disabled', 'disabled')
36
- button.classList.add('opacity-50', 'cursor-not-allowed')
43
+ button.classList.add(...this.disabledClasses)
37
44
  }
38
45
 
39
46
  enableButton(button) {
40
47
  button.removeAttribute('disabled')
41
- button.classList.remove('opacity-50', 'cursor-not-allowed')
48
+ button.classList.remove(...this.disabledClasses)
42
49
  }
43
50
  }
@@ -27,6 +27,8 @@ export default class extends Slideover {
27
27
 
28
28
  // Don't scroll the background when slideover is open
29
29
  lockScroll()
30
+
31
+ window.dispatchEvent(new Event('slideover:open'))
30
32
  }
31
33
 
32
34
  _hide() {
@@ -6,6 +6,8 @@ export default class extends Controller {
6
6
  static targets = ['add', 'remove']
7
7
  static values = {
8
8
  variantId: String,
9
+ createWishlistPath: String,
10
+ destroyWishlistPath: String
9
11
  }
10
12
 
11
13
  connect() {
@@ -27,7 +29,7 @@ export default class extends Controller {
27
29
 
28
30
  const headers = {}
29
31
 
30
- const response = await post('/account/wishlist/wished_items', {
32
+ const response = await post(this.createWishlistPathValue, {
31
33
  body: body,
32
34
  headers: headers,
33
35
  responseKind: 'turbo-stream'
@@ -45,7 +47,7 @@ export default class extends Controller {
45
47
 
46
48
  const headers = {}
47
49
 
48
- const response = await destroy(`/account/wishlist/wished_items/${this.variantIdValue}`, {
50
+ const response = await destroy(this.destroyWishlistPathValue, {
49
51
  headers: headers,
50
52
  responseKind: 'turbo-stream'
51
53
  })
@@ -1,6 +1,6 @@
1
1
  <div class="card-dialog mx-auto lg:w-1/3">
2
2
  <% unless params[:checkout] %>
3
- <h3 class="mb-5 font-semibold"><%= Spree.t(:create_account) %></h3>
3
+ <h3 class="mb-5 font-semibold"><%= Spree.t(:create_a_new_account) %></h3>
4
4
  <% end %>
5
5
  <%= render partial: 'spree/shared/error_messages', locals: { target: @user } %>
6
6
  <%= form_for resource, as: :spree_user, url: spree.registration_path(checkout: params[:checkout]), data: { turbo: false } do |f| %>
@@ -16,6 +16,6 @@
16
16
  </div>
17
17
  </div>
18
18
  </div>
19
- <%= f.submit Spree.t(:create_account), class: 'btn-primary block w-full lg:w-48 text-center mx-auto' %>
19
+ <%= f.submit Spree.t(:create_a_new_account), class: 'btn-primary block w-full lg:w-48 text-center mx-auto' %>
20
20
  <% end %>
21
21
  </div>
@@ -21,5 +21,21 @@
21
21
  </div>
22
22
  <% end %>
23
23
  <%= render "devise/shared/links" %>
24
+
25
+ <% if current_order.present? &&
26
+ current_store&.prefers_guest_checkout? &&
27
+ stored_location&.include?(spree.checkout_path(current_order.token))
28
+ %>
29
+ <div class="relative my-5 flex items-center">
30
+ <div class="flex-grow border-t border-gray-200"></div>
31
+ <span class="mx-4 text-sm text-gray-500 uppercase"><%= Spree.t(:or) %></span>
32
+ <div class="flex-grow border-t border-gray-200"></div>
33
+ </div>
34
+
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>
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
+ </div>
39
+ <% end %>
24
40
  </div>
25
41
  <% end %>
@@ -8,7 +8,7 @@
8
8
  <%= render 'spree/shared/head' %>
9
9
  <%= render 'spree/shared/custom_head' %>
10
10
  </head>
11
- <body id="checkout-page" class="w-full bg-background text-text">
11
+ <body id="checkout-page" class="theme-<%= current_theme.class.name.demodulize.underscore %> w-full bg-background text-text">
12
12
  <%= render_storefront_partials(:body_start_partials) %>
13
13
  <%= current_store.storefront_custom_code_body_start&.html_safe %>
14
14
 
@@ -6,7 +6,7 @@
6
6
  <%= render 'spree/shared/json_ld' %>
7
7
  <%= current_store.storefront_custom_code_head&.html_safe %>
8
8
  </head>
9
- <body class="bg-background w-full text-text <%= current_page&.slug %>-page <% if current_theme_preview.present? %>inside-page-builder<% end %>">
9
+ <body class="theme-<%= current_theme.class.name.demodulize.underscore %> bg-background w-full text-text <%= current_page&.slug %>-page <% if current_theme_preview.present? %>inside-page-builder<% end %>">
10
10
  <%= render_storefront_partials(:body_start_partials) %>
11
11
  <%= current_store.storefront_custom_code_body_start&.html_safe %>
12
12
 
@@ -5,4 +5,5 @@
5
5
  <%= turbo_stream.update "wishlist-icon" do %>
6
6
  <%= javascript_tag "wishedVariantIds = #{(current_wishlist.variant_ids).to_json.html_safe};" %>
7
7
  <%= render 'spree/shared/wishlist_icon' %>
8
+ <%= render_storefront_partials(:add_to_wishlist_partials) %>
8
9
  <% end %>
@@ -15,6 +15,10 @@
15
15
  </div>
16
16
  <% end %>
17
17
  <% end %>
18
+ <% elsif turbo_frame_request? %>
19
+ <%= turbo_frame_tag "edit_address_modal_#{@address.id}" do %>
20
+ <%= render partial: 'spree/account/addresses/edit_address_modal', locals: { address: @address } %>
21
+ <% end %>
18
22
  <% else %>
19
23
  <div class="page-container grid grid-cols-1 lg:grid-cols-12 lg:gap-6 lg:mt-6">
20
24
  <div class="lg:col-span-2">
@@ -15,6 +15,9 @@
15
15
  <% end %>
16
16
  </div>
17
17
  <% end %>
18
+ <%= turbo_frame_tag :new_address_modal do %>
19
+ <%= render partial: 'spree/account/addresses/new_address_modal', locals: { address: @address } %>
20
+ <% end %>
18
21
  <% else %>
19
22
  <% if params[:checkout].present? %>
20
23
  <div class="card-dialog mx-auto lg:w-1/3">
@@ -39,7 +39,7 @@
39
39
  <%= Spree.t(:ship_address) %>
40
40
  </div>
41
41
  <div class="px-5 word-break">
42
- <%= @order.ship_address.to_s.gsub('<br/>', ', ').html_safe %>
42
+ <%= sanitize(@order.ship_address.to_s.gsub('<br/>', ', ')) %>
43
43
  </div>
44
44
  </div>
45
45
  <div class="text-xs">
@@ -82,11 +82,13 @@
82
82
  <%= render partial: 'spree/checkout/payment', locals: { order: @order } %>
83
83
  <% elsif @order.delivery? %>
84
84
  <%= render partial: 'spree/checkout/delivery', locals: { order: @order } %>
85
- <% else %>
85
+ <% elsif step = @order.checkout_steps.find { |s| s == @order.state } %>
86
86
  <%= form_for @order, url: spree.update_checkout_path(@order.token, @order.state), html: { id: "checkout_form_#{@order.state}" } do |form| %>
87
87
  <div>
88
88
  <%= form.hidden_field :state_lock_version unless @order.errors.any? %>
89
- <%= render @order.state, form: form %>
89
+ <% if step.in?(%w[address delivery payment confirm]) %>
90
+ <%= render partial: "spree/checkout/#{step}", locals: { form: form } %>
91
+ <% end %>
90
92
  </div>
91
93
  <div>
92
94
  <%= button_tag Spree.t(:save_and_continue),
@@ -6,6 +6,7 @@
6
6
  <%= turbo_stream.replace_all '.cart-contents' do %>
7
7
  <%= turbo_frame_tag cart_id(@order), class: 'cart-contents' do %>
8
8
  <%= render 'spree/orders/cart' %>
9
+ <%= render_storefront_partials(:add_to_cart_partials) %>
9
10
  <% end %>
10
11
  <% end %>
11
12
 
@@ -5,6 +5,8 @@
5
5
  <% else %>
6
6
  <%= render 'spree/orders/cart' %>
7
7
  <% end %>
8
+
9
+ <%= render_storefront_partials(:remove_from_cart_partials) %>
8
10
  <% end %>
9
11
  <% end %>
10
12
 
@@ -1 +1,2 @@
1
- <%= render_page(current_page_or_preview, product: @product) %>
1
+ <%= render_page(current_page_or_preview, product: @product) %>
2
+ <%= render_storefront_partials(:product_partials) %>
@@ -1,10 +1,20 @@
1
1
  <li class="p-4 my-4 border border-accent lg:flex justify-between relative" id="<%= dom_id(address) %>">
2
2
  <%= render 'spree/shared/address', address: address %>
3
3
  <div class="flex gap-6 h-6 lg:h-10 mt-6 lg:mt-0">
4
- <%= link_to spree.edit_address_path(address), class: 'flex gap-2 lg:p-2' do %>
5
- <%= render 'spree/shared/icons/edit' %>
6
- <span class="uppercase text-sm font-semibold tracking-widest"><%= Spree.t(:edit_address) %></span>
7
- <% end %>
4
+ <div data-controller="modal" data-modal-allow-background-close="true">
5
+ <%= button_tag type: "button", class: 'flex gap-2 lg:p-2', data: { action: "click->modal#open" } do %>
6
+ <%= render 'spree/shared/icons/edit' %>
7
+ <span class="uppercase text-sm font-semibold tracking-widest"><%= Spree.t(:edit) %></span>
8
+ <% end %>
9
+ <%= turbo_frame_tag "edit_address_modal_#{address.id}",
10
+ src: spree.edit_address_path(address),
11
+ loading: :lazy,
12
+ data: {
13
+ modal_target: "container",
14
+ action: "click->modal#closeBackground keyup@window->modal#closeWithKeyboard"
15
+ },
16
+ class: "hidden animated fadeIn fixed inset-0 overflow-y-auto flex items-center justify-center z-[9999]" %>
17
+ </div>
8
18
  <div
9
19
  data-controller="modal"
10
20
  data-modal-allow-background-close="true"
@@ -0,0 +1,38 @@
1
+ <div class="bg-white h-[90vh] lg:h-[70vh] w-full lg:w-1/3 flex flex-col overflow-hidden">
2
+ <%= form_for @address, method: :put, html: { class: "flex flex-col h-full", data: { turbo_frame: "_top" } } do |f| %>
3
+ <div class="px-8 py-6 border-b shrink-0">
4
+ <div class="flex justify-between items-center">
5
+ <h3 class="text-xl lg:text-2xl font-medium lg:font-semibold capitalize">
6
+ <%= Spree.t(:edit_address) %>
7
+ </h3>
8
+ <button data-action="click->modal#close" class="ml-4">
9
+ <%= render 'spree/shared/icons/close' %>
10
+ <span class="sr-only"><%= Spree.t(:close) %></span>
11
+ </button>
12
+ </div>
13
+ <%= render 'spree/shared/error_messages', target: @address, class: 'alert-error text-center mt-4 p-2' %>
14
+ </div>
15
+ <div class="overflow-y-auto px-8 py-2 grow">
16
+ <div class="py-4 text-left">
17
+ <%= render 'spree/addresses/form',
18
+ address_name: 'address',
19
+ address_form: f,
20
+ address_type: 'shipping',
21
+ address: @address %>
22
+ </div>
23
+ <%= label_tag "default_shipping", class: "flex items-center gap-2 text-sm mb-4" do %>
24
+ <%= check_box_tag "default_shipping", "true", @address.user_default_shipping?, class: 'input-checkbox input-address-default', disabled: @address.user_default_shipping? %>
25
+ <%= Spree.t('address_book.set_as_default_delivery_address') %>
26
+ <% end %>
27
+ <%= label_tag "default_billing", class: "flex items-center gap-2 text-sm mb-4" do %>
28
+ <%= check_box_tag "default_billing", "true", @address.user_default_billing?, class: 'input-checkbox input-address-default', disabled: @address.user_default_billing? %>
29
+ <%= Spree.t('address_book.set_as_default_billing_address') %>
30
+ <% end %>
31
+ </div>
32
+ <div class="border-t border-default px-8 py-4 flex justify-end gap-8 shrink-0">
33
+ <%= button_tag Spree.t(:cancel), class: 'uppercase text-sm font-semibold', data: { action: 'click->modal#close' } %>
34
+ <%= hidden_field_tag :from_modal, "true" %>
35
+ <%= f.submit Spree.t(:update), class: 'btn-primary w-48' %>
36
+ </div>
37
+ <% end %>
38
+ </div>
@@ -0,0 +1,38 @@
1
+ <div class="bg-white h-[90vh] lg:h-[70vh] w-full lg:w-1/3 flex flex-col overflow-hidden">
2
+ <%= form_for @address, html: { class: "flex flex-col h-full", data: { turbo_frame: "_top" } } do |f| %>
3
+ <div class="px-8 py-6 border-b shrink-0">
4
+ <div class="flex justify-between items-center">
5
+ <h3 class="text-xl lg:text-2xl font-medium lg:font-semibold capitalize">
6
+ <%= Spree.t(:add_address) %>
7
+ </h3>
8
+ <button data-action="click->modal#close" class="ml-4">
9
+ <%= render 'spree/shared/icons/close' %>
10
+ <span class="sr-only"><%= Spree.t(:close) %></span>
11
+ </button>
12
+ </div>
13
+ <%= render 'spree/shared/error_messages', target: @address, class: 'alert-error text-center mt-4 p-2' %>
14
+ </div>
15
+ <div class="overflow-y-auto px-8 py-2 grow">
16
+ <div class="py-4 text-left">
17
+ <%= render 'spree/addresses/form',
18
+ address_name: 'address',
19
+ address_form: f,
20
+ address_type: 'shipping',
21
+ address: @address %>
22
+ </div>
23
+ <%= label_tag "default_shipping", class: "flex items-center gap-2 text-sm mb-4" do %>
24
+ <%= check_box_tag "default_shipping", "true", true, class: 'input-checkbox' %>
25
+ <%= Spree.t('address_book.set_as_default_delivery_address') %>
26
+ <% end %>
27
+ <%= label_tag "default_billing", class: "flex items-center gap-2 text-sm mb-4" do %>
28
+ <%= check_box_tag "default_billing", "true", true, class: 'input-checkbox' %>
29
+ <%= Spree.t('address_book.set_as_default_billing_address') %>
30
+ <% end %>
31
+ </div>
32
+ <div class="border-t border-default px-8 py-4 flex justify-end gap-8 shrink-0">
33
+ <%= button_tag Spree.t(:cancel), class: 'uppercase text-sm font-semibold', data: { action: 'click->modal#close' } %>
34
+ <%= hidden_field_tag :from_modal, "true" %>
35
+ <%= f.submit Spree.t(:add), class: 'btn-primary w-48', data: { test_id: 'add-address-button' } %>
36
+ </div>
37
+ <% end %>
38
+ </div>
@@ -12,14 +12,34 @@
12
12
  <%= render partial: 'spree/account/addresses/address', collection: @addresses, cached: ->a {[*spree_base_cache_scope.call(a), try_spree_current_user.ship_address_id, try_spree_current_user.bill_address_id]}, as: :address %>
13
13
  </ul>
14
14
  <div class="flex">
15
- <%= link_to Spree.t(:add_address), spree.new_address_path, class: 'btn-primary block w-full lg:w-auto' %>
15
+ <div data-controller="modal" data-modal-allow-background-close="true">
16
+ <%= button_tag Spree.t(:add), type: "button", class: "btn-primary block w-full lg:w-auto", data: { action: "click->modal#open" } %>
17
+ <%= turbo_frame_tag :new_address_modal,
18
+ src: spree.new_address_path,
19
+ loading: :lazy,
20
+ data: {
21
+ modal_target: "container",
22
+ action: "click->modal#closeBackground keyup@window->modal#closeWithKeyboard"
23
+ },
24
+ class: "hidden animated fadeIn fixed inset-0 overflow-y-auto flex items-center justify-center z-[9999]" %>
25
+ </div>
16
26
  </div>
17
27
  <% else %>
18
28
  <div class="text-center py-6">
19
29
  <p class="mb-2 font-medium uppercase"><%= Spree.t('storefront.account.no_addresses_title') %></p>
20
30
  <p><%= Spree.t('storefront.account.no_addresses_description') %></p>
21
31
  <div class="flex mt-8 justify-center">
22
- <%= link_to Spree.t(:add), spree.new_address_path, class: 'btn-primary block' %>
32
+ <div data-controller="modal" data-modal-allow-background-close="true">
33
+ <%= button_tag Spree.t(:add), type: "button", class: "btn-primary block", data: { action: "click->modal#open" } %>
34
+ <%= turbo_frame_tag :new_address_modal,
35
+ src: spree.new_address_path,
36
+ loading: :lazy,
37
+ data: {
38
+ modal_target: "container",
39
+ action: "click->modal#closeBackground keyup@window->modal#closeWithKeyboard"
40
+ },
41
+ class: "hidden animated fadeIn fixed inset-0 overflow-y-auto flex items-center justify-center z-[9999]" %>
42
+ </div>
23
43
  </div>
24
44
  </div>
25
45
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <div class="flex items-center">
2
2
  <%= form_for line_item, url: spree.line_item_url(line_item, order_token: line_item.order.token), data: { controller: 'turbo-stream-form' } do |item_form| %>
3
- <div class="quantity-picker" data-controller="quantity-picker">
3
+ <div class="quantity-picker" data-controller="quantity-picker" data-quantity-picker-disabled-class="opacity-50 cursor-not-allowed">
4
4
  <!-- this is a dummy button to work with turbo frame forms when hitting ENTER -->
5
5
  <%= button_tag render('spree/shared/icons/minus'), type: 'submit', class: 'hidden' %>
6
6
  <%= quantity_modifier_button_tag render('spree/shared/icons/minus'), type: 'submit', action: 'decrease', class: 'quantity-decrease-button' %>