spree_storefront 5.0.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 (341) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +13 -0
  3. data/Rakefile +15 -0
  4. data/app/assets/config/spree_storefront_manifest.js +6 -0
  5. data/app/assets/stylesheets/storefront_page_builder.css +51 -0
  6. data/app/controllers/concerns/spree/cart_methods.rb +40 -0
  7. data/app/controllers/concerns/spree/locale_urls.rb +23 -0
  8. data/app/controllers/concerns/spree/password_protected.rb +17 -0
  9. data/app/controllers/concerns/spree/theme_concern.rb +29 -0
  10. data/app/controllers/spree/account/addresses_controller.rb +16 -0
  11. data/app/controllers/spree/account/base_controller.rb +20 -0
  12. data/app/controllers/spree/account/newsletter_controller.rb +34 -0
  13. data/app/controllers/spree/account/orders_controller.rb +34 -0
  14. data/app/controllers/spree/account/profile_controller.rb +23 -0
  15. data/app/controllers/spree/account/store_credits_controller.rb +23 -0
  16. data/app/controllers/spree/account/wished_items_controller.rb +45 -0
  17. data/app/controllers/spree/addresses_controller.rb +101 -0
  18. data/app/controllers/spree/checkout_controller.rb +365 -0
  19. data/app/controllers/spree/contacts_controller.rb +46 -0
  20. data/app/controllers/spree/digital_links_controller.rb +48 -0
  21. data/app/controllers/spree/home_controller.rb +5 -0
  22. data/app/controllers/spree/line_items_controller.rb +96 -0
  23. data/app/controllers/spree/newsletter_subscribers_controller.rb +43 -0
  24. data/app/controllers/spree/order_status_controller.rb +32 -0
  25. data/app/controllers/spree/orders_controller.rb +104 -0
  26. data/app/controllers/spree/page_sections_controller.rb +17 -0
  27. data/app/controllers/spree/pages_controller.rb +13 -0
  28. data/app/controllers/spree/password_controller.rb +21 -0
  29. data/app/controllers/spree/policies_controller.rb +17 -0
  30. data/app/controllers/spree/posts_controller.rb +76 -0
  31. data/app/controllers/spree/products_controller.rb +99 -0
  32. data/app/controllers/spree/search_controller.rb +39 -0
  33. data/app/controllers/spree/seo_controller.rb +25 -0
  34. data/app/controllers/spree/settings_controller.rb +68 -0
  35. data/app/controllers/spree/store_controller.rb +247 -0
  36. data/app/controllers/spree/taxonomies_controller.rb +30 -0
  37. data/app/controllers/spree/taxons_controller.rb +46 -0
  38. data/app/controllers/spree/wishlists_controller.rb +19 -0
  39. data/app/finders/spree/storefront/variant_finder.rb +80 -0
  40. data/app/helpers/spree/analytics_helper.rb +28 -0
  41. data/app/helpers/spree/cart_helper.rb +48 -0
  42. data/app/helpers/spree/checkout_analytics_helper.rb +77 -0
  43. data/app/helpers/spree/checkout_helper.rb +58 -0
  44. data/app/helpers/spree/filters_helper.rb +180 -0
  45. data/app/helpers/spree/fonts_helper.rb +58 -0
  46. data/app/helpers/spree/orders_helper.rb +7 -0
  47. data/app/helpers/spree/page_helper.rb +67 -0
  48. data/app/helpers/spree/posts_helper.rb +42 -0
  49. data/app/helpers/spree/products_helper.rb +205 -0
  50. data/app/helpers/spree/storefront_helper.rb +86 -0
  51. data/app/helpers/spree/storefront_locale_helper.rb +42 -0
  52. data/app/helpers/spree/theme_helper.rb +244 -0
  53. data/app/helpers/spree/turbo_helper.rb +13 -0
  54. data/app/helpers/spree/turbo_stream_actions_helper.rb +13 -0
  55. data/app/helpers/spree/wishlist_helper.rb +7 -0
  56. data/app/javascript/spree/storefront/application.js +156 -0
  57. data/app/javascript/spree/storefront/controllers/accordion_controller.js +25 -0
  58. data/app/javascript/spree/storefront/controllers/account_nav_controller.js +10 -0
  59. data/app/javascript/spree/storefront/controllers/card_validation_controller.js +103 -0
  60. data/app/javascript/spree/storefront/controllers/carousel_controller.js +44 -0
  61. data/app/javascript/spree/storefront/controllers/cart_controller.js +10 -0
  62. data/app/javascript/spree/storefront/controllers/checkout_address_book_controller.js +28 -0
  63. data/app/javascript/spree/storefront/controllers/checkout_delivery_controller.js +39 -0
  64. data/app/javascript/spree/storefront/controllers/checkout_promotions_controller.js +28 -0
  65. data/app/javascript/spree/storefront/controllers/checkout_summary_controller.js +46 -0
  66. data/app/javascript/spree/storefront/controllers/clear_input_controller.js +23 -0
  67. data/app/javascript/spree/storefront/controllers/copy_input_controller.js +19 -0
  68. data/app/javascript/spree/storefront/controllers/dropdown_controller.js +14 -0
  69. data/app/javascript/spree/storefront/controllers/header_controller.js +33 -0
  70. data/app/javascript/spree/storefront/controllers/infinite_scroll_controller.js +31 -0
  71. data/app/javascript/spree/storefront/controllers/lightbox_controller.js +138 -0
  72. data/app/javascript/spree/storefront/controllers/mobile_nav_controller.js +17 -0
  73. data/app/javascript/spree/storefront/controllers/modal_controller.js +15 -0
  74. data/app/javascript/spree/storefront/controllers/no_ui_slider_controller.js +55 -0
  75. data/app/javascript/spree/storefront/controllers/pdp_desktop_gallery_controller.js +28 -0
  76. data/app/javascript/spree/storefront/controllers/plp_variant_picker_controller.js +151 -0
  77. data/app/javascript/spree/storefront/controllers/product_form_controller.js +136 -0
  78. data/app/javascript/spree/storefront/controllers/quantity_picker_controller.js +43 -0
  79. data/app/javascript/spree/storefront/controllers/search_suggestions_controller.js +61 -0
  80. data/app/javascript/spree/storefront/controllers/searchable_list_controller.js +25 -0
  81. data/app/javascript/spree/storefront/controllers/slideover_controller.js +40 -0
  82. data/app/javascript/spree/storefront/controllers/sticky_button_controller.js +32 -0
  83. data/app/javascript/spree/storefront/controllers/toggle_menu_controller.js +32 -0
  84. data/app/javascript/spree/storefront/controllers/turbo_stream_form_controller.js +51 -0
  85. data/app/javascript/spree/storefront/controllers/wished_item_controller.js +69 -0
  86. data/app/javascript/spree/storefront/helpers/lazy_load_controllers_with_manifest.js +78 -0
  87. data/app/javascript/spree/storefront/helpers/show_flash_message.js +25 -0
  88. data/app/models/spree/color_names.rb +35 -0
  89. data/app/models/spree/contact.rb +24 -0
  90. data/app/presenters/spree/colors_preview_styles_presenter.rb +84 -0
  91. data/app/presenters/spree/featured_product_presenter.rb +42 -0
  92. data/app/presenters/spree/mega_nav_presenter.rb +55 -0
  93. data/app/views/devise/passwords/edit.html.erb +22 -0
  94. data/app/views/devise/passwords/new.html.erb +16 -0
  95. data/app/views/devise/registrations/_form.html.erb +21 -0
  96. data/app/views/devise/registrations/edit.html.erb +24 -0
  97. data/app/views/devise/registrations/new.html.erb +27 -0
  98. data/app/views/devise/sessions/new.html.erb +25 -0
  99. data/app/views/devise/shared/_links.html.erb +22 -0
  100. data/app/views/layouts/spree/checkout.html.erb +58 -0
  101. data/app/views/layouts/spree/password.html.erb +34 -0
  102. data/app/views/layouts/spree/storefront.html.erb +38 -0
  103. data/app/views/spree/account/wished_items/create.turbo_stream.erb +8 -0
  104. data/app/views/spree/account/wished_items/destroy.turbo_stream.erb +20 -0
  105. data/app/views/spree/addresses/destroy.turbo_stream.erb +1 -0
  106. data/app/views/spree/addresses/edit.html.erb +57 -0
  107. data/app/views/spree/addresses/new.html.erb +71 -0
  108. data/app/views/spree/checkout/_address.html.erb +153 -0
  109. data/app/views/spree/checkout/_button_processing.html.erb +7 -0
  110. data/app/views/spree/checkout/_coupon_code.html.erb +55 -0
  111. data/app/views/spree/checkout/_credit_card.html.erb +12 -0
  112. data/app/views/spree/checkout/_delivery.html.erb +70 -0
  113. data/app/views/spree/checkout/_line_item.html.erb +26 -0
  114. data/app/views/spree/checkout/_line_items.html.erb +7 -0
  115. data/app/views/spree/checkout/_missing_all_line_items.html.erb +17 -0
  116. data/app/views/spree/checkout/_missing_line_items.html.erb +28 -0
  117. data/app/views/spree/checkout/_payment.html.erb +35 -0
  118. data/app/views/spree/checkout/_payment_methods.html.erb +72 -0
  119. data/app/views/spree/checkout/_payment_sources.html.erb +24 -0
  120. data/app/views/spree/checkout/_quick_checkout.html.erb +1 -0
  121. data/app/views/spree/checkout/_sidebar.html.erb +34 -0
  122. data/app/views/spree/checkout/_store_credit.html.erb +21 -0
  123. data/app/views/spree/checkout/_summary.html.erb +116 -0
  124. data/app/views/spree/checkout/_user_account.html.erb +9 -0
  125. data/app/views/spree/checkout/apply_coupon_code.turbo_stream.erb +45 -0
  126. data/app/views/spree/checkout/apply_store_credit.turbo_stream.erb +19 -0
  127. data/app/views/spree/checkout/edit.html.erb +98 -0
  128. data/app/views/spree/checkout/payment/_check.html.erb +0 -0
  129. data/app/views/spree/checkout/payment/_gateway.html.erb +74 -0
  130. data/app/views/spree/checkout/payment/_store_credit.html.erb +13 -0
  131. data/app/views/spree/checkout/remove_coupon_code.turbo_stream.erb +34 -0
  132. data/app/views/spree/checkout/remove_store_credit.turbo_stream.erb +19 -0
  133. data/app/views/spree/checkout/update.turbo_stream.erb +25 -0
  134. data/app/views/spree/home/index.html.erb +1 -0
  135. data/app/views/spree/line_items/create.turbo_stream.erb +13 -0
  136. data/app/views/spree/line_items/destroy.turbo_stream.erb +11 -0
  137. data/app/views/spree/line_items/update.turbo_stream.erb +10 -0
  138. data/app/views/spree/newsletter_subscribers/create.turbo_stream.erb +7 -0
  139. data/app/views/spree/order_status/new.html.erb +16 -0
  140. data/app/views/spree/page_sections/show.html.erb +3 -0
  141. data/app/views/spree/pages/show.html.erb +1 -0
  142. data/app/views/spree/password/show.html.erb +1 -0
  143. data/app/views/spree/posts/index.html.erb +1 -0
  144. data/app/views/spree/posts/related_products.html.erb +7 -0
  145. data/app/views/spree/posts/show.html.erb +1 -0
  146. data/app/views/spree/products/index.html.erb +1 -0
  147. data/app/views/spree/products/index.turbo_stream.erb +1 -0
  148. data/app/views/spree/products/related.html.erb +3 -0
  149. data/app/views/spree/products/show.html.erb +1 -0
  150. data/app/views/spree/search/show.html.erb +6 -0
  151. data/app/views/spree/search/show.turbo_stream.erb +1 -0
  152. data/app/views/spree/search/suggestions.turbo_stream.erb +8 -0
  153. data/app/views/spree/seo/robots.text.erb +31 -0
  154. data/app/views/spree/seo/sitemap.xml.erb +25 -0
  155. data/app/views/spree/shared/_fonts.html.erb +14 -0
  156. data/app/views/spree/shared/_head.html.erb +36 -0
  157. data/app/views/spree/shared/_load_more_products.turbo_stream.erb +7 -0
  158. data/app/views/spree/shared/_product_listing_page.html.erb +11 -0
  159. data/app/views/spree/shared/_products.html.erb +1 -0
  160. data/app/views/spree/taxonomies/show.html.erb +1 -0
  161. data/app/views/spree/taxons/show.html.erb +1 -0
  162. data/app/views/spree/taxons/show.turbo_stream.erb +1 -0
  163. data/app/views/spree/waitlists/create.turbo_stream.erb +9 -0
  164. data/app/views/themes/default/kaminari/storefront/_first_page.html.erb +8 -0
  165. data/app/views/themes/default/kaminari/storefront/_gap.html.erb +8 -0
  166. data/app/views/themes/default/kaminari/storefront/_last_page.html.erb +8 -0
  167. data/app/views/themes/default/kaminari/storefront/_next_page.html.erb +13 -0
  168. data/app/views/themes/default/kaminari/storefront/_page.html.erb +10 -0
  169. data/app/views/themes/default/kaminari/storefront/_paginator.html.erb +27 -0
  170. data/app/views/themes/default/kaminari/storefront/_prev_page.html.erb +13 -0
  171. data/app/views/themes/default/spree/account/_account_nav.html.erb +46 -0
  172. data/app/views/themes/default/spree/account/_order.html.erb +39 -0
  173. data/app/views/themes/default/spree/account/_orders.html.erb +16 -0
  174. data/app/views/themes/default/spree/account/addresses/_address.html.erb +42 -0
  175. data/app/views/themes/default/spree/account/addresses/index.html.erb +28 -0
  176. data/app/views/themes/default/spree/account/newsletter/_newsletter_settings.html.erb +13 -0
  177. data/app/views/themes/default/spree/account/newsletter/edit.html.erb +16 -0
  178. data/app/views/themes/default/spree/account/newsletter/update.html.erb +1 -0
  179. data/app/views/themes/default/spree/account/orders/index.html.erb +24 -0
  180. data/app/views/themes/default/spree/account/orders/show.html.erb +22 -0
  181. data/app/views/themes/default/spree/account/profile/edit.html.erb +36 -0
  182. data/app/views/themes/default/spree/account/store_credits/_store_credit_event.html.erb +29 -0
  183. data/app/views/themes/default/spree/account/store_credits/index.html.erb +31 -0
  184. data/app/views/themes/default/spree/checkout/_footer.html.erb +10 -0
  185. data/app/views/themes/default/spree/checkout/complete.html.erb +84 -0
  186. data/app/views/themes/default/spree/contacts/new.html.erb +23 -0
  187. data/app/views/themes/default/spree/orders/_cart.html.erb +14 -0
  188. data/app/views/themes/default/spree/orders/_empty.html.erb +11 -0
  189. data/app/views/themes/default/spree/orders/_line_item.html.erb +47 -0
  190. data/app/views/themes/default/spree/orders/_line_item_quantity.html.erb +11 -0
  191. data/app/views/themes/default/spree/orders/_summary.html.erb +41 -0
  192. data/app/views/themes/default/spree/orders/edit.html.erb +18 -0
  193. data/app/views/themes/default/spree/orders/show.html.erb +6 -0
  194. data/app/views/themes/default/spree/page_sections/_announcement_bar.html.erb +10 -0
  195. data/app/views/themes/default/spree/page_sections/_custom_code.html.erb +5 -0
  196. data/app/views/themes/default/spree/page_sections/_featured_product.html.erb +136 -0
  197. data/app/views/themes/default/spree/page_sections/_featured_taxon.html.erb +116 -0
  198. data/app/views/themes/default/spree/page_sections/_featured_taxons.html.erb +71 -0
  199. data/app/views/themes/default/spree/page_sections/_footer.html.erb +62 -0
  200. data/app/views/themes/default/spree/page_sections/_header.html.erb +166 -0
  201. data/app/views/themes/default/spree/page_sections/_image_banner.html.erb +57 -0
  202. data/app/views/themes/default/spree/page_sections/_image_with_text.html.erb +66 -0
  203. data/app/views/themes/default/spree/page_sections/_main_password_footer.html.erb +64 -0
  204. data/app/views/themes/default/spree/page_sections/_main_password_header.html.erb +54 -0
  205. data/app/views/themes/default/spree/page_sections/_newsletter.html.erb +47 -0
  206. data/app/views/themes/default/spree/page_sections/_page_title.html.erb +7 -0
  207. data/app/views/themes/default/spree/page_sections/_post_details.html.erb +19 -0
  208. data/app/views/themes/default/spree/page_sections/_post_grid.html.erb +11 -0
  209. data/app/views/themes/default/spree/page_sections/_product_details.html.erb +102 -0
  210. data/app/views/themes/default/spree/page_sections/_product_grid.html.erb +52 -0
  211. data/app/views/themes/default/spree/page_sections/_related_products.html.erb +15 -0
  212. data/app/views/themes/default/spree/page_sections/_rich_text.html.erb +18 -0
  213. data/app/views/themes/default/spree/page_sections/_taxon_banner.html.erb +37 -0
  214. data/app/views/themes/default/spree/page_sections/_taxon_grid.html.erb +103 -0
  215. data/app/views/themes/default/spree/page_sections/_video.html.erb +27 -0
  216. data/app/views/themes/default/spree/page_sections/nav/_desktop.html.erb +49 -0
  217. data/app/views/themes/default/spree/page_sections/nav/_mobile.html.erb +136 -0
  218. data/app/views/themes/default/spree/policies/show.html.erb +11 -0
  219. data/app/views/themes/default/spree/posts/_json_ld.html.erb +20 -0
  220. data/app/views/themes/default/spree/posts/_pagination.html.erb +1 -0
  221. data/app/views/themes/default/spree/posts/_post.html.erb +13 -0
  222. data/app/views/themes/default/spree/products/_add_to_cart_button.html.erb +58 -0
  223. data/app/views/themes/default/spree/products/_add_to_waitlist.html.erb +36 -0
  224. data/app/views/themes/default/spree/products/_add_to_wishlist.html.erb +33 -0
  225. data/app/views/themes/default/spree/products/_breadcrumbs.html.erb +23 -0
  226. data/app/views/themes/default/spree/products/_color_picker.html.erb +19 -0
  227. data/app/views/themes/default/spree/products/_color_swatches.html.erb +61 -0
  228. data/app/views/themes/default/spree/products/_details.html.erb +55 -0
  229. data/app/views/themes/default/spree/products/_featured_image.html.erb +37 -0
  230. data/app/views/themes/default/spree/products/_filters.html.erb +45 -0
  231. data/app/views/themes/default/spree/products/_json_ld.html.erb +38 -0
  232. data/app/views/themes/default/spree/products/_json_ld_list.html.erb +9 -0
  233. data/app/views/themes/default/spree/products/_json_ld_variant.html.erb +10 -0
  234. data/app/views/themes/default/spree/products/_label.html.erb +26 -0
  235. data/app/views/themes/default/spree/products/_media_gallery.html.erb +94 -0
  236. data/app/views/themes/default/spree/products/_price.html.erb +59 -0
  237. data/app/views/themes/default/spree/products/_product.html.erb +62 -0
  238. data/app/views/themes/default/spree/products/_quantity_selector.html.erb +32 -0
  239. data/app/views/themes/default/spree/products/_returns_policy_modal.html.erb +22 -0
  240. data/app/views/themes/default/spree/products/_show_more_button.html.erb +8 -0
  241. data/app/views/themes/default/spree/products/_sort.html.erb +45 -0
  242. data/app/views/themes/default/spree/products/_swiper.html.erb +85 -0
  243. data/app/views/themes/default/spree/products/_tags.html.erb +0 -0
  244. data/app/views/themes/default/spree/products/_variant_options.html.erb +71 -0
  245. data/app/views/themes/default/spree/products/_variant_picker.html.erb +50 -0
  246. data/app/views/themes/default/spree/products/filters/_availability.html.erb +32 -0
  247. data/app/views/themes/default/spree/products/filters/_colors.html.erb +23 -0
  248. data/app/views/themes/default/spree/products/filters/_generic.html.erb +75 -0
  249. data/app/views/themes/default/spree/products/filters/_price.html.erb +35 -0
  250. data/app/views/themes/default/spree/products/filters/_taxons.erb +78 -0
  251. data/app/views/themes/default/spree/search/_suggestions.html.erb +92 -0
  252. data/app/views/themes/default/spree/settings/show.html.erb +32 -0
  253. data/app/views/themes/default/spree/shared/_account_pane.html.erb +28 -0
  254. data/app/views/themes/default/spree/shared/_address.html.erb +35 -0
  255. data/app/views/themes/default/spree/shared/_cart_icon.html.erb +13 -0
  256. data/app/views/themes/default/spree/shared/_cart_pane.html.erb +38 -0
  257. data/app/views/themes/default/spree/shared/_css_variables.html.erb +54 -0
  258. data/app/views/themes/default/spree/shared/_custom_head.html.erb +0 -0
  259. data/app/views/themes/default/spree/shared/_error_messages.html.erb +9 -0
  260. data/app/views/themes/default/spree/shared/_error_messages_without_base_attribute.html.erb +15 -0
  261. data/app/views/themes/default/spree/shared/_flash.html.erb +16 -0
  262. data/app/views/themes/default/spree/shared/_flashes.html.erb +10 -0
  263. data/app/views/themes/default/spree/shared/_json_ld.html.erb +32 -0
  264. data/app/views/themes/default/spree/shared/_line_item_options.html.erb +18 -0
  265. data/app/views/themes/default/spree/shared/_logo.html.erb +42 -0
  266. data/app/views/themes/default/spree/shared/_meta_tags.html.erb +45 -0
  267. data/app/views/themes/default/spree/shared/_order_details.html.erb +106 -0
  268. data/app/views/themes/default/spree/shared/_order_line_item.html.erb +65 -0
  269. data/app/views/themes/default/spree/shared/_order_shipment.html.erb +71 -0
  270. data/app/views/themes/default/spree/shared/_search.html.erb +32 -0
  271. data/app/views/themes/default/spree/shared/_title.html.erb +3 -0
  272. data/app/views/themes/default/spree/shared/_wishlist_icon.html.erb +13 -0
  273. data/app/views/themes/default/spree/shared/icons/_account.html.erb +17 -0
  274. data/app/views/themes/default/spree/shared/icons/_arrow-left.html.erb +8 -0
  275. data/app/views/themes/default/spree/shared/icons/_arrow-right.html.erb +3 -0
  276. data/app/views/themes/default/spree/shared/icons/_bell.html.erb +9 -0
  277. data/app/views/themes/default/spree/shared/icons/_cart.html.erb +10 -0
  278. data/app/views/themes/default/spree/shared/icons/_cart_48.html.erb +6 -0
  279. data/app/views/themes/default/spree/shared/icons/_check.html.erb +4 -0
  280. data/app/views/themes/default/spree/shared/icons/_chevron.html.erb +15 -0
  281. data/app/views/themes/default/spree/shared/icons/_chevron_down.html.erb +5 -0
  282. data/app/views/themes/default/spree/shared/icons/_chevron_right.html.erb +15 -0
  283. data/app/views/themes/default/spree/shared/icons/_chevron_up.html.erb +3 -0
  284. data/app/views/themes/default/spree/shared/icons/_close.html.erb +9 -0
  285. data/app/views/themes/default/spree/shared/icons/_cross.html.erb +16 -0
  286. data/app/views/themes/default/spree/shared/icons/_delete.html.erb +3 -0
  287. data/app/views/themes/default/spree/shared/icons/_delivery.html.erb +5 -0
  288. data/app/views/themes/default/spree/shared/icons/_disabled.html.erb +13 -0
  289. data/app/views/themes/default/spree/shared/icons/_edit.html.erb +3 -0
  290. data/app/views/themes/default/spree/shared/icons/_facebook.html.erb +16 -0
  291. data/app/views/themes/default/spree/shared/icons/_filter.html.erb +8 -0
  292. data/app/views/themes/default/spree/shared/icons/_heart.html.erb +12 -0
  293. data/app/views/themes/default/spree/shared/icons/_info.html.erb +7 -0
  294. data/app/views/themes/default/spree/shared/icons/_instagram.html.erb +18 -0
  295. data/app/views/themes/default/spree/shared/icons/_lock.html.erb +13 -0
  296. data/app/views/themes/default/spree/shared/icons/_menu.html.erb +10 -0
  297. data/app/views/themes/default/spree/shared/icons/_minus.html.erb +5 -0
  298. data/app/views/themes/default/spree/shared/icons/_pinch.html.erb +6 -0
  299. data/app/views/themes/default/spree/shared/icons/_pinterest.html.erb +8 -0
  300. data/app/views/themes/default/spree/shared/icons/_plus.html.erb +17 -0
  301. data/app/views/themes/default/spree/shared/icons/_return.html.erb +11 -0
  302. data/app/views/themes/default/spree/shared/icons/_search.html.erb +17 -0
  303. data/app/views/themes/default/spree/shared/icons/_spinner.html.erb +1 -0
  304. data/app/views/themes/default/spree/shared/icons/_spotify.html.erb +8 -0
  305. data/app/views/themes/default/spree/shared/icons/_tiktok.html.erb +9 -0
  306. data/app/views/themes/default/spree/shared/icons/_twitter.html.erb +16 -0
  307. data/app/views/themes/default/spree/shared/icons/_youtube.html.erb +9 -0
  308. data/app/views/themes/default/spree/shared/icons/_zoom.html.erb +10 -0
  309. data/app/views/themes/default/spree/wishlists/_no_wished_items.html.erb +10 -0
  310. data/app/views/themes/default/spree/wishlists/_wished_item.html.erb +38 -0
  311. data/app/views/themes/default/spree/wishlists/show.html.erb +17 -0
  312. data/config/i18n-tasks.yml +176 -0
  313. data/config/importmap.rb +22 -0
  314. data/config/initializers/assets.rb +1 -0
  315. data/config/initializers/heroicon.rb +10 -0
  316. data/config/locales/en.yml +76 -0
  317. data/config/routes.rb +88 -0
  318. data/lib/generators/spree/storefront/install/install_generator.rb +45 -0
  319. data/lib/generators/spree/storefront/install/templates/application.tailwind.css +1760 -0
  320. data/lib/generators/spree/storefront/install/templates/dev +16 -0
  321. data/lib/generators/spree/storefront/install/templates/tailwind.config.js +128 -0
  322. data/lib/generators/spree/storefront/theme/templates/model.rb.tt +12 -0
  323. data/lib/generators/spree/storefront/theme/theme_generator.rb +41 -0
  324. data/lib/spree/storefront/configuration.rb +11 -0
  325. data/lib/spree/storefront/engine.rb +51 -0
  326. data/lib/spree/storefront/testing_support/capybara_utils.rb +13 -0
  327. data/lib/spree/storefront.rb +16 -0
  328. data/lib/spree_storefront.rb +1 -0
  329. data/vendor/colornames.json +1 -0
  330. data/vendor/javascript/@kanety--stimulus-accordion.js +4 -0
  331. data/vendor/javascript/@stimulus-components--carousel.js +4 -0
  332. data/vendor/javascript/card-validator.js +4 -0
  333. data/vendor/javascript/credit-card-type.js +4 -0
  334. data/vendor/javascript/headroom.js.js +19 -0
  335. data/vendor/javascript/nouislider.js +4 -0
  336. data/vendor/javascript/photoswipe--dist--photoswipe-lightbox.esm.js.js +667 -0
  337. data/vendor/javascript/photoswipe.js +1675 -0
  338. data/vendor/javascript/stimulus-read-more.js +4 -0
  339. data/vendor/javascript/stimulus-scroll-to.js +4 -0
  340. data/vendor/javascript/swiper--bundle.js +4 -0
  341. metadata +567 -0
@@ -0,0 +1,247 @@
1
+ module Spree
2
+ class StoreController < BaseController
3
+ include Spree::Core::ControllerHelpers::Order
4
+
5
+ include Spree::LocaleUrls
6
+ include Spree::ThemeConcern
7
+ include Spree::StorefrontHelper
8
+ include Spree::PasswordProtected
9
+ include Spree::WishlistHelper
10
+ include Spree::AnalyticsHelper
11
+
12
+ layout :choose_layout
13
+
14
+ helper 'spree/base'
15
+ helper 'spree/locale'
16
+ helper 'spree/storefront_locale'
17
+ helper 'spree/currency'
18
+ helper 'spree/addresses'
19
+ helper 'spree/wishlist'
20
+
21
+ helper_method :title
22
+ helper_method :title=
23
+ helper_method :accurate_title
24
+
25
+ helper_method :current_taxon, :store_filter_names
26
+
27
+ helper_method :permitted_products_params, :products_filters_params,
28
+ :storefront_products_scope, :storefront_products,
29
+ :default_products_sort, :default_products_finder_params
30
+
31
+ before_action :redirect_to_default_locale
32
+ before_action :render_404_if_store_not_exists
33
+ rescue_from ActionController::InvalidAuthenticityToken, with: :invalid_authenticity_token
34
+
35
+ def render_404_if_store_not_exists
36
+ return if current_store.present?
37
+
38
+ render 'errors/404', layout: 'application', status: :not_found
39
+ end
40
+
41
+ def current_taxon
42
+ @current_taxon ||= (current_store.taxons.find_by(id: params[:taxon_id]) if params[:taxon_id].present?)
43
+ end
44
+
45
+ def store_filter_names
46
+ @store_filter_names ||= Spree::OptionType.filterable.order(:position).pluck(:name)
47
+ end
48
+
49
+ def store_filter_names_hash
50
+ filter_names = {}
51
+ store_filter_names.each do |filter_name|
52
+ filter_names[filter_name.to_sym] = []
53
+ end
54
+ filter_names
55
+ end
56
+
57
+ def permitted_products_params
58
+ @permitted_products_params ||= params.permit(
59
+ :locale,
60
+ :currency,
61
+ :q,
62
+ :page,
63
+ :per_page,
64
+ :sort_by,
65
+ filter: [
66
+ :min_price,
67
+ :max_price,
68
+ :purchasable,
69
+ :out_of_stock,
70
+ {
71
+ options: [store_filter_names_hash],
72
+ taxon_ids: []
73
+ }
74
+ ]
75
+ )
76
+ end
77
+
78
+ def default_products_sort
79
+ 'manual'
80
+ end
81
+
82
+ def products_filters_params
83
+ @products_filters_params ||= permitted_products_params[:filter]&.compact_blank || {}
84
+ end
85
+
86
+ protected
87
+
88
+ def choose_layout
89
+ if turbo_frame_request? && !current_theme_preview.present?
90
+ 'turbo_rails/frame'
91
+ else
92
+ 'spree/storefront'
93
+ end
94
+ end
95
+
96
+ # can be used in views as well as controllers.
97
+ # e.g. <% self.title = 'This is a custom title for this view' %>
98
+ attr_writer :title
99
+
100
+ def title
101
+ title_string = @title.present? ? @title : accurate_title
102
+ if title_string.present?
103
+ title_string
104
+ else
105
+ default_title
106
+ end
107
+ end
108
+
109
+ def default_title
110
+ @default_title ||= current_store.name
111
+ end
112
+
113
+ # this is a hook for subclasses to provide title
114
+ def accurate_title
115
+ @accurate_title ||= current_store.seo_title.presence || current_store.name
116
+ end
117
+
118
+ def storefront_products_scope
119
+ current_store.products.active(current_currency)
120
+ end
121
+
122
+ def default_products_finder_params
123
+ taxon = @taxon || current_taxon
124
+
125
+ filter = permitted_products_params.fetch(:filter, {}).dup
126
+
127
+ filter[:taxon_ids] ||= [taxon&.id.to_s].compact
128
+ filter[:taxons] = filter[:taxon_ids].join(',')
129
+
130
+ if filter.key?(:min_price) || filter.key?(:max_price)
131
+ min_price = filter[:min_price].presence || 0
132
+ max_price = filter[:max_price].presence || 'Infinity'
133
+
134
+ filter[:price] = [min_price, max_price].compact.join(',')
135
+ end
136
+
137
+ permitted_products_params.merge(
138
+ store: current_store,
139
+ filter: filter,
140
+ currency: current_currency
141
+ )
142
+ end
143
+
144
+ def storefront_products
145
+ @storefront_products ||= begin
146
+ finder_params = default_products_finder_params
147
+ finder_params[:sort_by] ||= @taxon&.sort_order || 'manual'
148
+
149
+ products_finder = Spree::Dependencies.products_finder.constantize
150
+ products = products_finder.
151
+ new(scope: storefront_products_scope, params: finder_params).
152
+ execute.
153
+ includes(storefront_products_includes)
154
+
155
+ default_per_page = Spree::Storefront::Config[:products_per_page]
156
+ per_page = params[:per_page].present? ? params[:per_page].to_i : default_per_page
157
+ page = params[:page].present? ? params[:page].to_i : 1
158
+
159
+ products.page(page).per(per_page)
160
+ end
161
+ end
162
+
163
+ def storefront_products_includes
164
+ [
165
+ :prices_including_master,
166
+ :variant_images,
167
+ :option_types,
168
+ :option_values,
169
+ { master: [:images, :prices, :stock_items, :stock_locations, { stock_items: :stock_location }],
170
+ variants: [
171
+ :images, :prices, :option_values, :stock_items, :stock_locations,
172
+ { option_values: :option_type, stock_items: :stock_location }
173
+ ],
174
+ taxons: [:taxonomy],
175
+ taggings: [:tag] }
176
+ ]
177
+ end
178
+
179
+ def redirect_unauthorized_access
180
+ if try_spree_current_user
181
+ flash[:error] = Spree.t(:authorization_failure)
182
+ redirect_to spree.forbidden_path
183
+ else
184
+ store_location
185
+ if respond_to?(:spree_login_path)
186
+ redirect_to spree_login_path
187
+ else
188
+ redirect_to spree.root_path
189
+ end
190
+ end
191
+ end
192
+
193
+ def redirect_back_or_default(default)
194
+ redirect_to(session[:user_return_to] || default)
195
+ end
196
+
197
+ def require_user(return_to = nil)
198
+ return if try_spree_current_user
199
+
200
+ store_location(return_to)
201
+
202
+ respond_to do |format|
203
+ format.html { redirect_to spree_login_path }
204
+ format.turbo_stream { render turbo_stream: turbo_stream.slideover_open('slideover-account', 'account-pane') }
205
+ end
206
+ end
207
+
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)
213
+ return unless defined?(Devise)
214
+
215
+ location ||= request.fullpath
216
+
217
+ store_location_for(Devise.mappings.keys.first, location)
218
+ end
219
+
220
+ def redirect_to_cart
221
+ redirect_to spree.cart_path
222
+ end
223
+
224
+ def clear_order_token
225
+ cookies.delete(:token)
226
+ end
227
+
228
+ def disable_sesion_tracking
229
+ request.session_options[:skip] = true
230
+ end
231
+
232
+ def invalid_authenticity_token(exception)
233
+ Rails.error.report(
234
+ exception,
235
+ context: { user_id: spree_current_user&.id },
236
+ source: 'spree.storefront'
237
+ )
238
+
239
+ flash[:error] = Spree.t(:something_went_wrong)
240
+
241
+ respond_to do |format|
242
+ format.html { redirect_back_or_to root_path }
243
+ format.turbo_stream { render turbo_stream: turbo_stream.update('flash', partial: 'spree/shared/flashes') }
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ class TaxonomiesController < Spree::StoreController
3
+ def show
4
+ @taxonomy = current_store.taxons.where(parent_id: nil).friendly.find(params[:id])
5
+
6
+ @taxons = @taxonomy.children.order(name: :asc)
7
+ taxons_grouped = @taxons.group_by { |b| b.name[0].upcase }
8
+
9
+ numbers = ('0'..'9').to_a
10
+ alphabet_letters = ('A'..'Z').to_a
11
+
12
+ @taxons_grouped_by_letter = begin
13
+ taxons_grouped_by_letter = {}
14
+ alphabet_letters.each do |letter|
15
+ taxons_grouped_by_letter[letter] = taxons_grouped[letter]
16
+ end
17
+ taxons_grouped_by_letter
18
+ end
19
+
20
+ @taxons_grouped_by_numbers = taxons_grouped.select { |k, _v| numbers.include?(k) }
21
+ @taxons_grouped_by_other = taxons_grouped.select { |k, _v| alphabet_letters.concat(numbers).exclude?(k) }
22
+ end
23
+
24
+ private
25
+
26
+ def accurate_title
27
+ @taxonomy.root.seo_title.presence || @taxonomy.root.name
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ module Spree
2
+ class TaxonsController < Spree::StoreController
3
+ include Spree::StorefrontHelper
4
+ helper 'spree/products'
5
+
6
+ before_action :load_taxon
7
+ after_action :track_show, only: :show
8
+
9
+ def show
10
+ redirect_if_legacy_path unless turbo_frame_request? || turbo_stream_request?
11
+
12
+ @current_page = current_theme.pages.find_by(type: 'Spree::Pages::Taxon')
13
+ end
14
+
15
+ private
16
+
17
+ def accurate_title
18
+ load_taxon unless @taxon
19
+ @taxon.seo_title
20
+ end
21
+
22
+ def load_taxon
23
+ @taxon ||= find_with_fallback_default_locale { current_store.taxons.friendly.find(params[:id]) }
24
+ end
25
+
26
+ def track_show
27
+ return if turbo_frame_request? || turbo_stream_request?
28
+
29
+ track_event('product_list_viewed', { taxon: @taxon })
30
+ end
31
+
32
+ def redirect_if_legacy_path
33
+ # If an old id or a numeric id was used to find the record,
34
+ # we should do a 301 redirect that uses the current friendly id.
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
39
+ end
40
+ end
41
+
42
+ def default_products_sort
43
+ @taxon.sort_order
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ class WishlistsController < StoreController
3
+ def show
4
+ if params[:id].present? && params[:token].present?
5
+ @wishlist = current_store.wishlists.find_by!(id: params[:id], token: params[:token])
6
+ elsif try_spree_current_user
7
+ # https://github.com/spree/spree/blob/9475c6633b762669ee0c8f1f8a4d73e1c221a94e/core/app/models/concerns/spree/user_methods.rb#L71
8
+ @wishlist = try_spree_current_user.default_wishlist_for_store(current_store)
9
+ else
10
+ return require_user
11
+ end
12
+
13
+ @wished_items = @wishlist.wished_items.includes(
14
+ product: [:prices_including_master, :variants, :master, :taggings],
15
+ variant: [:images, :product, { option_values: :option_type }]
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,80 @@
1
+ module Spree
2
+ module Storefront
3
+ class VariantFinder
4
+ def initialize(product:, current_currency:, variant_id: nil, options_hash: {})
5
+ @product = product
6
+ @variant_id = variant_id
7
+ @current_currency = current_currency
8
+ @options_hash = options_hash
9
+ end
10
+
11
+ def find
12
+ try_variant_from_options
13
+ return [@selected_variant, @variant_from_options] if @selected_variant.present?
14
+
15
+ try_any_variant
16
+ [@selected_variant, @variant_from_options]
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :current_currency, :options_hash, :variant_id
22
+
23
+ def try_variant_from_options
24
+ return unless options_hash.present?
25
+ return unless (@product.option_type_ids.map(&:to_s) - options_hash.keys).empty?
26
+
27
+ @product.variants.each do |variant|
28
+ options_matched = options_hash.all? do |option_type_id, name|
29
+ variant.option_values.any? { |ov| ov.option_type_id.to_s == option_type_id && ov.name.downcase.strip == name.downcase.strip }
30
+ end
31
+
32
+ if options_matched && variant.purchasable? && variant.price_in(current_currency).amount.present?
33
+ @selected_variant = variant
34
+ @variant_from_options = variant
35
+ break
36
+ elsif options_matched
37
+ @variant_from_options = variant
38
+ break
39
+ end
40
+ end
41
+ end
42
+
43
+ def try_any_variant
44
+ if @product.has_variants?
45
+ if options_hash.present? # if selected combination is not purchasable nor active
46
+ @selected_variant = nil
47
+ elsif variant_id.present?
48
+ variant = @product.variants.find(variant_id)
49
+ @variant_from_options = variant
50
+ @selected_variant = variant
51
+ elsif product_option_types.size == 1 && product_option_types.first.color?
52
+ @selected_variant = @variant_from_options = @product.variants.find { |v| v.purchasable? && v.price_in(current_currency).amount.present? }
53
+ elsif @product.variants.size == 1 || product_has_only_one_secondary_option_type_value?
54
+ @variant_from_options = @product.variants.first
55
+ @selected_variant = @variant_from_options
56
+ end
57
+ elsif @product.master.purchasable? && @product.master.price_in(current_currency).amount.present?
58
+ @selected_variant = @variant_from_options = @product.master
59
+ else
60
+ @selected_variant = nil
61
+ @variant_from_options = @product.master
62
+ end
63
+ end
64
+
65
+ def product_has_only_one_secondary_option_type_value?
66
+ return false unless product_option_types.size == 2
67
+ return false unless product_option_types.first.color?
68
+
69
+ @product.
70
+ option_values.
71
+ select { |ov| ov.option_type_id == product_option_types.last.id }.
72
+ uniq(&:id).size == 1
73
+ end
74
+
75
+ def product_option_types
76
+ @product_option_types ||= @product.option_types.to_a
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,28 @@
1
+ module Spree
2
+ module AnalyticsHelper
3
+ def analytics_event_handlers
4
+ @analytics_event_handlers ||= Spree::Analytics.event_handlers.map do |handler|
5
+ handler.new(user: try_spree_current_user, session: session, request: request)
6
+ end
7
+ end
8
+
9
+ def track_event(event_name, record)
10
+ return if current_theme_preview.present?
11
+ return if unsupported_event?(event_name)
12
+
13
+ analytics_event_handlers.each do |handler|
14
+ handler.handle_event(event_name, record)
15
+ end
16
+ rescue => e
17
+ Rails.error.report(
18
+ e,
19
+ context: { event_name: event_name, record_id: record&.id, record_type: record&.class&.name },
20
+ source: 'spree.storefront'
21
+ )
22
+ end
23
+
24
+ def unsupported_event?(event_name)
25
+ !Spree::Analytics.events.key?(event_name.to_sym)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ module Spree
2
+ module CartHelper
3
+ def quantity_field_options(opts = {})
4
+ opts[:min] ||= 1
5
+ opts[:max] ||= maximum_quantity
6
+
7
+ {
8
+ min: opts[:min], max: opts[:max],
9
+ class: opts[:class],
10
+ data: { 'quantity-picker-target': 'quantity' },
11
+ aria: { label: Spree.t(:quantity) }
12
+ }
13
+ end
14
+
15
+ def quantity_modifier_button_tag(text = '+', opts = {})
16
+ opts[:action] ||= 'increase'
17
+ opts[:type] ||= 'button'
18
+
19
+ button_tag(
20
+ text,
21
+ type: opts[:type],
22
+ class: opts[:class],
23
+ data: {
24
+ 'quantity-picker-target': opts[:action],
25
+ action: "click->quantity-picker##{opts[:action]} click->cart#disableCart"
26
+ }
27
+ )
28
+ end
29
+
30
+ def color_options_style_for_line_items(line_items)
31
+ @color_options_style_for_line_items = begin
32
+ colors = line_items.map(&:variant).map do |v|
33
+ color_option_values = v.option_values.find_all do |ov|
34
+ ov.option_type.color?
35
+ end
36
+
37
+ color_option_values.map do |ov|
38
+ { name: ov.name, filter_name: ov.name }
39
+ end
40
+ end
41
+
42
+ colors = colors.flatten.uniq { |color| [color[:name], color[:filter_name]] }
43
+
44
+ Spree::ColorsPreviewStylesPresenter.new(colors).to_s
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,77 @@
1
+ module Spree
2
+ module CheckoutAnalyticsHelper
3
+ def coupon_tracking_params
4
+ {
5
+ order: @order,
6
+ coupon_code: params[:coupon_code],
7
+ coupon_handler: @result
8
+ }
9
+ end
10
+
11
+ def clean_analytics_session
12
+ session.delete(:checkout_started)
13
+ session.delete(:checkout_step_viewed)
14
+ session.delete(:checkout_step_completed)
15
+ end
16
+
17
+ def clear_checkout_completed_session
18
+ session.delete(:checkout_completed)
19
+ end
20
+
21
+ def order_tracking_params
22
+ tracking_params = {
23
+ last_ip_address: request.remote_ip,
24
+ }
25
+
26
+ tracking_params[:ahoy_visit_id] = current_visit.id if @order.respond_to?(:ahoy_visit) && defined?(current_visit) && current_visit&.id
27
+ tracking_params
28
+ end
29
+
30
+ def track_checkout_started
31
+ # server-side tracking
32
+ track_event('checkout_started', { order: @order })
33
+
34
+ # client-side tracking
35
+ session[:checkout_started] = true
36
+ end
37
+
38
+ def track_checkout_entered_email
39
+ return unless @order.email.present?
40
+ return unless params.dig(:order, :email).present?
41
+
42
+ track_event('checkout_email_entered', { order: @order, email: @order.email })
43
+ end
44
+
45
+ def track_payment_info_entered
46
+ return unless @order.payment_method.present?
47
+ return unless params.dig(:order, :payments_attributes).present?
48
+
49
+ track_event('payment_info_entered', { order: @order })
50
+ end
51
+
52
+ def track_checkout_step_viewed
53
+ # server-side tracking
54
+ track_event('checkout_step_viewed', { order: @order, step: params[:state] || @order.state })
55
+
56
+ # client-side tracking
57
+ trackable_checkout_steps = @order.checkout_steps[0..2]
58
+ session[:checkout_step_viewed] = true if trackable_checkout_steps.include? @order.state
59
+ end
60
+
61
+ def track_checkout_step_completed
62
+ # server-side tracking
63
+ track_event('checkout_step_completed', { order: @order, step: @previous_state })
64
+
65
+ # client-side tracking
66
+ session[:checkout_step_completed] = @previous_state # for 3rd party frontend tracking
67
+ end
68
+
69
+ def track_checkout_completed
70
+ # server-side tracking
71
+ track_event('checkout_completed', { order: @order })
72
+
73
+ # client-side tracking
74
+ session[:checkout_completed] = true
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,58 @@
1
+ module Spree
2
+ module CheckoutHelper
3
+ def checkout_progress(numbers: false)
4
+ states = (@order.checkout_steps - ['complete']).unshift('cart')
5
+ states -= ['delivery'] if @order.digital?
6
+
7
+ items = states.each_with_index.map do |state, i|
8
+ text = Spree.t("order_state.#{state}").titleize
9
+ text.prepend("#{i.succ}. ") if numbers
10
+
11
+ css_classes = ['breadcrumb-item']
12
+
13
+ if @order.passed_checkout_step?(state)
14
+ link_content = text
15
+ link_url = if state == 'cart'
16
+ spree.cart_url(host: current_store.url_or_custom_domain, order_token: @order.token)
17
+ else
18
+ spree.checkout_state_path(@order.token, state)
19
+ end
20
+
21
+ text = link_to(link_content, link_url)
22
+ css_classes << 'text-primary'
23
+ content_tag('li', text, class: css_classes.join(' '))
24
+ else
25
+ content_tag('li', text, class: "breadcrumb-item #{state == @order.state ? 'font-bold' : 'text-text'}")
26
+ end
27
+ end
28
+ content_tag('ol', raw(items.join("\n")), class: 'breadcrumb flex items-center py-6 gap-2', id: "checkout-step-#{@order.state}")
29
+ end
30
+
31
+ def checkout_available_payment_methods
32
+ @checkout_available_payment_methods ||= @order.available_payment_methods.reject(&:store_credit?)
33
+ end
34
+
35
+ def checkout_started?
36
+ @order.state == 'address' && @order.state_was == 'cart'
37
+ end
38
+
39
+ def already_have_an_account?
40
+ @already_have_an_account ||= @order.email.present? && Spree.user_class.exists?(email: @order.email.downcase)
41
+ end
42
+
43
+ def checkout_payment_sources(payment_method = nil)
44
+ return [] unless try_spree_current_user.respond_to?(:payment_sources)
45
+
46
+ payment_method.present? ? try_spree_current_user.payment_sources.where(payment_method: payment_method) : try_spree_current_user.payment_sources
47
+ end
48
+
49
+ def quick_checkout_enabled?(order)
50
+ order.payment_required? && order.shipments.count <= 1 &&
51
+ (order.digital? || !order.some_digital?) # Either fully digital or not digital at all
52
+ end
53
+
54
+ def can_use_store_credit_on_checkout?(order)
55
+ order.could_use_store_credit? && (!order.respond_to?(:gift_card) || !order.gift_card.present?)
56
+ end
57
+ end
58
+ end