spree_admin 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 (795) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +13 -0
  3. data/README.md +4 -0
  4. data/Rakefile +15 -0
  5. data/app/assets/config/spree_admin_manifest.js +10 -0
  6. data/app/assets/images/favicon_256x256.png +0 -0
  7. data/app/assets/stylesheets/spree/admin/actiontext.css +36 -0
  8. data/app/assets/stylesheets/spree/admin/application.scss +43 -0
  9. data/app/assets/stylesheets/spree/admin/components/_alerts.scss +48 -0
  10. data/app/assets/stylesheets/spree/admin/components/_badges.scss +117 -0
  11. data/app/assets/stylesheets/spree/admin/components/_bulk_panel.scss +31 -0
  12. data/app/assets/stylesheets/spree/admin/components/_buttons.scss +198 -0
  13. data/app/assets/stylesheets/spree/admin/components/_cards.scss +126 -0
  14. data/app/assets/stylesheets/spree/admin/components/_dropdowns.scss +148 -0
  15. data/app/assets/stylesheets/spree/admin/components/_filters.scss +41 -0
  16. data/app/assets/stylesheets/spree/admin/components/_line_items_form.scss +32 -0
  17. data/app/assets/stylesheets/spree/admin/components/_main.scss +148 -0
  18. data/app/assets/stylesheets/spree/admin/components/_media_form.scss +91 -0
  19. data/app/assets/stylesheets/spree/admin/components/_modals.scss +42 -0
  20. data/app/assets/stylesheets/spree/admin/components/_navbar.scss +10 -0
  21. data/app/assets/stylesheets/spree/admin/components/_navigation.scss +53 -0
  22. data/app/assets/stylesheets/spree/admin/components/_offcanvas.scss +26 -0
  23. data/app/assets/stylesheets/spree/admin/components/_search_picker.scss +55 -0
  24. data/app/assets/stylesheets/spree/admin/components/_seo_form.scss +16 -0
  25. data/app/assets/stylesheets/spree/admin/components/_sortable_tree.scss +58 -0
  26. data/app/assets/stylesheets/spree/admin/components/_tables.scss +187 -0
  27. data/app/assets/stylesheets/spree/admin/components/_variants_form.scss +203 -0
  28. data/app/assets/stylesheets/spree/admin/global/_variables.scss +190 -0
  29. data/app/assets/stylesheets/spree/admin/plugins/_dropdown.scss +102 -0
  30. data/app/assets/stylesheets/spree/admin/plugins/_items.scss +115 -0
  31. data/app/assets/stylesheets/spree/admin/plugins/_tinymce_custom.scss +74 -0
  32. data/app/assets/stylesheets/spree/admin/plugins/_trix_custom.scss +110 -0
  33. data/app/assets/stylesheets/spree/admin/plugins/flatfile.scss +4 -0
  34. data/app/assets/stylesheets/spree/admin/plugins/plugins/checkbox_options.scss +5 -0
  35. data/app/assets/stylesheets/spree/admin/plugins/plugins/clear_button.scss +30 -0
  36. data/app/assets/stylesheets/spree/admin/plugins/plugins/drag_drop.scss +16 -0
  37. data/app/assets/stylesheets/spree/admin/plugins/plugins/dropdown_header.scss +23 -0
  38. data/app/assets/stylesheets/spree/admin/plugins/plugins/dropdown_input.scss +47 -0
  39. data/app/assets/stylesheets/spree/admin/plugins/plugins/input_autogrow.scss +18 -0
  40. data/app/assets/stylesheets/spree/admin/plugins/plugins/optgroup_columns.scss +23 -0
  41. data/app/assets/stylesheets/spree/admin/plugins/plugins/remove_button.scss +44 -0
  42. data/app/assets/stylesheets/spree/admin/plugins/tom-select.bootstrap4.scss +219 -0
  43. data/app/assets/stylesheets/spree/admin/plugins/tom-select.bootstrap5.scss +274 -0
  44. data/app/assets/stylesheets/spree/admin/plugins/tom-select.default.scss +87 -0
  45. data/app/assets/stylesheets/spree/admin/plugins/tom-select.scss +175 -0
  46. data/app/assets/stylesheets/spree/admin/plugins/uppy_fixes.scss +25 -0
  47. data/app/assets/stylesheets/spree/admin/shared/_base.scss +674 -0
  48. data/app/assets/stylesheets/spree/admin/shared/_forms.scss +184 -0
  49. data/app/assets/stylesheets/spree/admin/views/_dashboard.scss +34 -0
  50. data/app/assets/stylesheets/spree/admin/views/_page_builder.scss +237 -0
  51. data/app/controllers/concerns/spree/admin/analytics_concern.rb +51 -0
  52. data/app/controllers/concerns/spree/admin/bulk_operations_concern.rb +38 -0
  53. data/app/controllers/concerns/spree/admin/order_concern.rb +24 -0
  54. data/app/controllers/concerns/spree/admin/page_builder_concern.rb +44 -0
  55. data/app/controllers/spree/admin/action_text/video_embeds_controller.rb +44 -0
  56. data/app/controllers/spree/admin/addresses_controller.rb +86 -0
  57. data/app/controllers/spree/admin/assets_controller.rb +70 -0
  58. data/app/controllers/spree/admin/base_controller.rb +131 -0
  59. data/app/controllers/spree/admin/checkouts_controller.rb +23 -0
  60. data/app/controllers/spree/admin/classifications_controller.rb +74 -0
  61. data/app/controllers/spree/admin/coupon_codes_controller.rb +21 -0
  62. data/app/controllers/spree/admin/custom_domains_controller.rb +19 -0
  63. data/app/controllers/spree/admin/customer_returns_controller.rb +20 -0
  64. data/app/controllers/spree/admin/dashboard_controller.rb +145 -0
  65. data/app/controllers/spree/admin/digital_assets_controller.rb +45 -0
  66. data/app/controllers/spree/admin/errors_controller.rb +11 -0
  67. data/app/controllers/spree/admin/exports_controller.rb +48 -0
  68. data/app/controllers/spree/admin/line_items_controller.rb +121 -0
  69. data/app/controllers/spree/admin/oauth_applications_controller.rb +27 -0
  70. data/app/controllers/spree/admin/option_types_controller.rb +23 -0
  71. data/app/controllers/spree/admin/option_values_controller.rb +21 -0
  72. data/app/controllers/spree/admin/orders/billing_address_controller.rb +103 -0
  73. data/app/controllers/spree/admin/orders/contact_information_controller.rb +45 -0
  74. data/app/controllers/spree/admin/orders/customer_returns_controller.rb +81 -0
  75. data/app/controllers/spree/admin/orders/payment_links_controller.rb +33 -0
  76. data/app/controllers/spree/admin/orders/return_authorizations_controller.rb +90 -0
  77. data/app/controllers/spree/admin/orders/shipping_address_controller.rb +85 -0
  78. data/app/controllers/spree/admin/orders/user_controller.rb +94 -0
  79. data/app/controllers/spree/admin/orders_controller.rb +87 -0
  80. data/app/controllers/spree/admin/page_blocks_controller.rb +40 -0
  81. data/app/controllers/spree/admin/page_links_controller.rb +43 -0
  82. data/app/controllers/spree/admin/page_sections_controller.rb +57 -0
  83. data/app/controllers/spree/admin/pages_controller.rb +40 -0
  84. data/app/controllers/spree/admin/payment_methods_controller.rb +45 -0
  85. data/app/controllers/spree/admin/payments_controller.rb +122 -0
  86. data/app/controllers/spree/admin/post_categories_controller.rb +6 -0
  87. data/app/controllers/spree/admin/posts_controller.rb +23 -0
  88. data/app/controllers/spree/admin/products_controller.rb +366 -0
  89. data/app/controllers/spree/admin/profile_controller.rb +31 -0
  90. data/app/controllers/spree/admin/promotion_actions_controller.rb +47 -0
  91. data/app/controllers/spree/admin/promotion_rules_controller.rb +39 -0
  92. data/app/controllers/spree/admin/promotions_controller.rb +61 -0
  93. data/app/controllers/spree/admin/properties_controller.rb +23 -0
  94. data/app/controllers/spree/admin/refund_reasons_controller.rb +6 -0
  95. data/app/controllers/spree/admin/refunds_controller.rb +43 -0
  96. data/app/controllers/spree/admin/reimbursement_types_controller.rb +6 -0
  97. data/app/controllers/spree/admin/reimbursements_controller.rb +47 -0
  98. data/app/controllers/spree/admin/reports_controller.rb +52 -0
  99. data/app/controllers/spree/admin/resource_controller.rb +325 -0
  100. data/app/controllers/spree/admin/return_authorization_reasons_controller.rb +6 -0
  101. data/app/controllers/spree/admin/return_authorizations_controller.rb +29 -0
  102. data/app/controllers/spree/admin/return_items_controller.rb +9 -0
  103. data/app/controllers/spree/admin/roles_controller.rb +6 -0
  104. data/app/controllers/spree/admin/shipments_controller.rb +102 -0
  105. data/app/controllers/spree/admin/shipping_categories_controller.rb +6 -0
  106. data/app/controllers/spree/admin/shipping_methods_controller.rb +29 -0
  107. data/app/controllers/spree/admin/stock_items_controller.rb +25 -0
  108. data/app/controllers/spree/admin/stock_locations_controller.rb +24 -0
  109. data/app/controllers/spree/admin/stock_transfers_controller.rb +48 -0
  110. data/app/controllers/spree/admin/store_credit_categories_controller.rb +6 -0
  111. data/app/controllers/spree/admin/store_credits_controller.rb +93 -0
  112. data/app/controllers/spree/admin/storefront_controller.rb +38 -0
  113. data/app/controllers/spree/admin/stores_controller.rb +87 -0
  114. data/app/controllers/spree/admin/tax_categories_controller.rb +6 -0
  115. data/app/controllers/spree/admin/tax_rates_controller.rb +20 -0
  116. data/app/controllers/spree/admin/taxonomies_controller.rb +21 -0
  117. data/app/controllers/spree/admin/taxons_controller.rb +98 -0
  118. data/app/controllers/spree/admin/themes_controller.rb +75 -0
  119. data/app/controllers/spree/admin/translations_controller.rb +74 -0
  120. data/app/controllers/spree/admin/users_controller.rb +91 -0
  121. data/app/controllers/spree/admin/variants_controller.rb +90 -0
  122. data/app/controllers/spree/admin/webhooks_subscribers_controller.rb +48 -0
  123. data/app/controllers/spree/admin/zones_controller.rb +37 -0
  124. data/app/helpers/spree/admin/base_helper.rb +246 -0
  125. data/app/helpers/spree/admin/custom_domains_helper.rb +9 -0
  126. data/app/helpers/spree/admin/customer_returns_helper.rb +18 -0
  127. data/app/helpers/spree/admin/flash_helper.rb +15 -0
  128. data/app/helpers/spree/admin/navigation_helper.rb +195 -0
  129. data/app/helpers/spree/admin/onboarding_helper.rb +18 -0
  130. data/app/helpers/spree/admin/orders_filters_helper.rb +68 -0
  131. data/app/helpers/spree/admin/orders_helper.rb +179 -0
  132. data/app/helpers/spree/admin/page_builder_helper.rb +130 -0
  133. data/app/helpers/spree/admin/payments_helper.rb +24 -0
  134. data/app/helpers/spree/admin/posts_helper.rb +9 -0
  135. data/app/helpers/spree/admin/products_helper.rb +127 -0
  136. data/app/helpers/spree/admin/promotion_actions_helper.rb +10 -0
  137. data/app/helpers/spree/admin/promotion_rules_helper.rb +17 -0
  138. data/app/helpers/spree/admin/promotions_helper.rb +25 -0
  139. data/app/helpers/spree/admin/reimbursement_type_helper.rb +9 -0
  140. data/app/helpers/spree/admin/session_assets_helper.rb +24 -0
  141. data/app/helpers/spree/admin/shipment_helper.rb +27 -0
  142. data/app/helpers/spree/admin/sortable_tree_helper.rb +31 -0
  143. data/app/helpers/spree/admin/stock_locations_helper.rb +22 -0
  144. data/app/helpers/spree/admin/store_helper.rb +46 -0
  145. data/app/helpers/spree/admin/stores_helper.rb +86 -0
  146. data/app/helpers/spree/admin/tags_helper.rb +35 -0
  147. data/app/helpers/spree/admin/taxons_helper.rb +27 -0
  148. data/app/helpers/spree/admin/themes_helper.rb +9 -0
  149. data/app/helpers/spree/admin/translations_helper.rb +14 -0
  150. data/app/helpers/spree/admin/turbo_helper.rb +41 -0
  151. data/app/helpers/spree/admin/users_helper.rb +37 -0
  152. data/app/helpers/spree/admin/webhooks_subscribers_helper.rb +31 -0
  153. data/app/helpers/tom_select_helper.rb +42 -0
  154. data/app/javascript/spree/admin/application.js +157 -0
  155. data/app/javascript/spree/admin/controllers/active_storage_upload_controller.js +168 -0
  156. data/app/javascript/spree/admin/controllers/address_form_controller.js +98 -0
  157. data/app/javascript/spree/admin/controllers/asset_uploader_controller.js +58 -0
  158. data/app/javascript/spree/admin/controllers/autocomplete_select_controller.js +36 -0
  159. data/app/javascript/spree/admin/controllers/better_slider_controller.js +23 -0
  160. data/app/javascript/spree/admin/controllers/block_form_controller.js +11 -0
  161. data/app/javascript/spree/admin/controllers/bootstrap_tabs_controller.js +9 -0
  162. data/app/javascript/spree/admin/controllers/bulk_operation_controller.js +78 -0
  163. data/app/javascript/spree/admin/controllers/calculator_fields_controller.js +24 -0
  164. data/app/javascript/spree/admin/controllers/calendar_range_controller.js +52 -0
  165. data/app/javascript/spree/admin/controllers/clipboard_controller.js +17 -0
  166. data/app/javascript/spree/admin/controllers/color_palette_controller.js +22 -0
  167. data/app/javascript/spree/admin/controllers/color_picker_controller.js +52 -0
  168. data/app/javascript/spree/admin/controllers/enable_button_controller.js +29 -0
  169. data/app/javascript/spree/admin/controllers/filters_controller.js +66 -0
  170. data/app/javascript/spree/admin/controllers/font_picker_controller.js +55 -0
  171. data/app/javascript/spree/admin/controllers/media_form_controller.js +38 -0
  172. data/app/javascript/spree/admin/controllers/multi_input_controller.js +75 -0
  173. data/app/javascript/spree/admin/controllers/multi_tom_select_controller.js +80 -0
  174. data/app/javascript/spree/admin/controllers/order_billing_address_controller.js +39 -0
  175. data/app/javascript/spree/admin/controllers/page_builder_controller.js +316 -0
  176. data/app/javascript/spree/admin/controllers/password_toggle_controller.js +14 -0
  177. data/app/javascript/spree/admin/controllers/product_form_controller.js +70 -0
  178. data/app/javascript/spree/admin/controllers/range_input_controller.js +9 -0
  179. data/app/javascript/spree/admin/controllers/replace_controller.js +15 -0
  180. data/app/javascript/spree/admin/controllers/return_items_controller.js +47 -0
  181. data/app/javascript/spree/admin/controllers/row_link_controller.js +18 -0
  182. data/app/javascript/spree/admin/controllers/rule_form_controller.js +17 -0
  183. data/app/javascript/spree/admin/controllers/search_picker_controller.js +38 -0
  184. data/app/javascript/spree/admin/controllers/section_form_controller.js +14 -0
  185. data/app/javascript/spree/admin/controllers/select_controller.js +117 -0
  186. data/app/javascript/spree/admin/controllers/seo_form_controller.js +116 -0
  187. data/app/javascript/spree/admin/controllers/slug_form_controller.js +17 -0
  188. data/app/javascript/spree/admin/controllers/sortable_tree_controller.js +55 -0
  189. data/app/javascript/spree/admin/controllers/stock_transfer_controller.js +87 -0
  190. data/app/javascript/spree/admin/controllers/store_form_controller.js +17 -0
  191. data/app/javascript/spree/admin/controllers/unit_system_controller.js +27 -0
  192. data/app/javascript/spree/admin/controllers/variants_form_controller.js +1048 -0
  193. data/app/javascript/spree/admin/controllers/webhook_subscriber_events_controller.js +19 -0
  194. data/app/javascript/spree/admin/helpers/bootstrap.js +49 -0
  195. data/app/javascript/spree/admin/helpers/canvas.js +29 -0
  196. data/app/javascript/spree/admin/helpers/index.js +0 -0
  197. data/app/javascript/spree/admin/helpers/tinymce.js +65 -0
  198. data/app/javascript/spree/admin/helpers/trix/video_embed.js +182 -0
  199. data/app/javascript/spree/admin/helpers/uppy_active_storage.js +210 -0
  200. data/app/models/spree/admin/resource.rb +36 -0
  201. data/app/models/spree/admin/updater.rb +42 -0
  202. data/app/views/active_storage/_upload_form.html.erb +65 -0
  203. data/app/views/kaminari/admin-twitter-bootstrap-4/_first_page.html.erb +13 -0
  204. data/app/views/kaminari/admin-twitter-bootstrap-4/_gap.html.erb +12 -0
  205. data/app/views/kaminari/admin-twitter-bootstrap-4/_last_page.html.erb +13 -0
  206. data/app/views/kaminari/admin-twitter-bootstrap-4/_next_page.html.erb +13 -0
  207. data/app/views/kaminari/admin-twitter-bootstrap-4/_page.html.erb +18 -0
  208. data/app/views/kaminari/admin-twitter-bootstrap-4/_paginator.html.erb +17 -0
  209. data/app/views/kaminari/admin-twitter-bootstrap-4/_prev_page.html.erb +13 -0
  210. data/app/views/layouts/spree/admin.html.erb +21 -0
  211. data/app/views/layouts/spree/page_builder.html.erb +45 -0
  212. data/app/views/spree/admin/addresses/edit.html.erb +12 -0
  213. data/app/views/spree/admin/addresses/new.html.erb +16 -0
  214. data/app/views/spree/admin/assets/bulk_destroy.turbo_stream.erb +3 -0
  215. data/app/views/spree/admin/assets/create.turbo_stream.erb +5 -0
  216. data/app/views/spree/admin/assets/destroy.turbo_stream.erb +4 -0
  217. data/app/views/spree/admin/assets/edit.html.erb +27 -0
  218. data/app/views/spree/admin/assets/update.turbo_stream.erb +7 -0
  219. data/app/views/spree/admin/classifications/_classification.html.erb +38 -0
  220. data/app/views/spree/admin/classifications/create.turbo_stream.erb +7 -0
  221. data/app/views/spree/admin/classifications/destroy.turbo_stream.erb +2 -0
  222. data/app/views/spree/admin/classifications/index.html.erb +20 -0
  223. data/app/views/spree/admin/classifications/new.html.erb +1 -0
  224. data/app/views/spree/admin/coupon_codes/_coupon_code.html.erb +12 -0
  225. data/app/views/spree/admin/coupon_codes/index.csv.erb +14 -0
  226. data/app/views/spree/admin/coupon_codes/index.html.erb +70 -0
  227. data/app/views/spree/admin/custom_domains/_custom_domain.html.erb +13 -0
  228. data/app/views/spree/admin/custom_domains/_custom_domains.html.erb +24 -0
  229. data/app/views/spree/admin/custom_domains/_form.html.erb +19 -0
  230. data/app/views/spree/admin/custom_domains/create.turbo_stream.erb +3 -0
  231. data/app/views/spree/admin/custom_domains/edit.html.erb +15 -0
  232. data/app/views/spree/admin/custom_domains/index.html.erb +50 -0
  233. data/app/views/spree/admin/custom_domains/new.html.erb +15 -0
  234. data/app/views/spree/admin/customer_returns/_customer_return.html.erb +31 -0
  235. data/app/views/spree/admin/customer_returns/index.html.erb +57 -0
  236. data/app/views/spree/admin/dashboard/_analytics.html.erb +3 -0
  237. data/app/views/spree/admin/dashboard/_setup_progress.html.erb +17 -0
  238. data/app/views/spree/admin/dashboard/_setup_tasks.html.erb +24 -0
  239. data/app/views/spree/admin/dashboard/_store_preview.html.erb +45 -0
  240. data/app/views/spree/admin/dashboard/_top_products.html.erb +71 -0
  241. data/app/views/spree/admin/dashboard/_updater.html.erb +9 -0
  242. data/app/views/spree/admin/dashboard/_visits.html.erb +112 -0
  243. data/app/views/spree/admin/dashboard/analytics.html.erb +118 -0
  244. data/app/views/spree/admin/dashboard/getting_started.html.erb +22 -0
  245. data/app/views/spree/admin/dashboard/setup_tasks/_add_products.html.erb +22 -0
  246. data/app/views/spree/admin/dashboard/setup_tasks/_set_customer_support_email.html.erb +23 -0
  247. data/app/views/spree/admin/dashboard/setup_tasks/_setup_payment_method.html.erb +6 -0
  248. data/app/views/spree/admin/dashboard/setup_tasks/_setup_taxes_collection.html.erb +6 -0
  249. data/app/views/spree/admin/dashboard/show.html.erb +33 -0
  250. data/app/views/spree/admin/digital_assets/_digital_asset.html.erb +30 -0
  251. data/app/views/spree/admin/digital_assets/_form.html.erb +18 -0
  252. data/app/views/spree/admin/digital_assets/_table.html.erb +40 -0
  253. data/app/views/spree/admin/digital_assets/create.turbo_stream.erb +2 -0
  254. data/app/views/spree/admin/digital_assets/edit.html.erb +22 -0
  255. data/app/views/spree/admin/digital_assets/index.html.erb +27 -0
  256. data/app/views/spree/admin/digital_assets/new.html.erb +22 -0
  257. data/app/views/spree/admin/digital_assets/update.turbo_stream.erb +2 -0
  258. data/app/views/spree/admin/exports/_export.html.erb +42 -0
  259. data/app/views/spree/admin/exports/create.turbo_stream.erb +2 -0
  260. data/app/views/spree/admin/exports/index.html.erb +30 -0
  261. data/app/views/spree/admin/exports/new.html.erb +42 -0
  262. data/app/views/spree/admin/line_items/edit.html.erb +20 -0
  263. data/app/views/spree/admin/line_items/new.html.erb +32 -0
  264. data/app/views/spree/admin/oauth_applications/_form.html.erb +11 -0
  265. data/app/views/spree/admin/oauth_applications/create.turbo_stream.erb +41 -0
  266. data/app/views/spree/admin/oauth_applications/edit.html.erb +19 -0
  267. data/app/views/spree/admin/oauth_applications/index.html.erb +75 -0
  268. data/app/views/spree/admin/oauth_applications/new.html.erb +25 -0
  269. data/app/views/spree/admin/option_types/_filter.html.erb +7 -0
  270. data/app/views/spree/admin/option_types/_form.html.erb +87 -0
  271. data/app/views/spree/admin/option_types/_option_type.html.erb +19 -0
  272. data/app/views/spree/admin/option_types/_option_value_fields.html.erb +29 -0
  273. data/app/views/spree/admin/option_types/edit.html.erb +11 -0
  274. data/app/views/spree/admin/option_types/index.html.erb +63 -0
  275. data/app/views/spree/admin/option_types/new.html.erb +11 -0
  276. data/app/views/spree/admin/option_types/update.turbo_stream.erb +1 -0
  277. data/app/views/spree/admin/option_values/destroy.turbo_stream.erb +1 -0
  278. data/app/views/spree/admin/orders/_body_custom.html.erb +0 -0
  279. data/app/views/spree/admin/orders/_customer.html.erb +79 -0
  280. data/app/views/spree/admin/orders/_customer_returns.html.erb +26 -0
  281. data/app/views/spree/admin/orders/_customer_summary.html.erb +22 -0
  282. data/app/views/spree/admin/orders/_extra_filters.html.erb +0 -0
  283. data/app/views/spree/admin/orders/_filters.html.erb +106 -0
  284. data/app/views/spree/admin/orders/_header.html.erb +76 -0
  285. data/app/views/spree/admin/orders/_header_custom.html.erb +0 -0
  286. data/app/views/spree/admin/orders/_line_item.html.erb +61 -0
  287. data/app/views/spree/admin/orders/_line_items.html.erb +68 -0
  288. data/app/views/spree/admin/orders/_list.html.erb +30 -0
  289. data/app/views/spree/admin/orders/_order.html.erb +28 -0
  290. data/app/views/spree/admin/orders/_payments.html.erb +35 -0
  291. data/app/views/spree/admin/orders/_refunds.html.erb +49 -0
  292. data/app/views/spree/admin/orders/_return_authorizations.html.erb +26 -0
  293. data/app/views/spree/admin/orders/_risk_analysis.html.erb +26 -0
  294. data/app/views/spree/admin/orders/_shipment.html.erb +104 -0
  295. data/app/views/spree/admin/orders/_shipment_manifest_item.html.erb +17 -0
  296. data/app/views/spree/admin/orders/_shipments.html.erb +22 -0
  297. data/app/views/spree/admin/orders/_summary.html.erb +100 -0
  298. data/app/views/spree/admin/orders/_summary_custom.html.erb +0 -0
  299. data/app/views/spree/admin/orders/_table_filter_dropdown.html.erb +34 -0
  300. data/app/views/spree/admin/orders/_tax_lines.html.erb +11 -0
  301. data/app/views/spree/admin/orders/_user_overview.html.erb +13 -0
  302. data/app/views/spree/admin/orders/billing_address/_form.html.erb +14 -0
  303. data/app/views/spree/admin/orders/billing_address/create.turbo_stream.erb +35 -0
  304. data/app/views/spree/admin/orders/billing_address/edit.html.erb +42 -0
  305. data/app/views/spree/admin/orders/billing_address/new.html.erb +35 -0
  306. data/app/views/spree/admin/orders/billing_address/update.turbo_stream.erb +35 -0
  307. data/app/views/spree/admin/orders/contact_information/edit.html.erb +20 -0
  308. data/app/views/spree/admin/orders/customer_returns/_customer_return.html.erb +70 -0
  309. data/app/views/spree/admin/orders/customer_returns/_reimbursements_table.html.erb +40 -0
  310. data/app/views/spree/admin/orders/customer_returns/_return_item_decision.html.erb +54 -0
  311. data/app/views/spree/admin/orders/customer_returns/_return_item_selection.html.erb +72 -0
  312. data/app/views/spree/admin/orders/customer_returns/edit.html.erb +144 -0
  313. data/app/views/spree/admin/orders/customer_returns/new.html.erb +67 -0
  314. data/app/views/spree/admin/orders/edit.html.erb +23 -0
  315. data/app/views/spree/admin/orders/index.html.erb +23 -0
  316. data/app/views/spree/admin/orders/return_authorizations/_form.html.erb +176 -0
  317. data/app/views/spree/admin/orders/return_authorizations/_return_authorization.html.erb +36 -0
  318. data/app/views/spree/admin/orders/return_authorizations/edit.html.erb +44 -0
  319. data/app/views/spree/admin/orders/return_authorizations/new.html.erb +13 -0
  320. data/app/views/spree/admin/orders/return_authorizations/show.html.erb +128 -0
  321. data/app/views/spree/admin/orders/shipping_address/_form.html.erb +13 -0
  322. data/app/views/spree/admin/orders/shipping_address/create.turbo_stream.erb +17 -0
  323. data/app/views/spree/admin/orders/shipping_address/edit.html.erb +17 -0
  324. data/app/views/spree/admin/orders/shipping_address/new.html.erb +17 -0
  325. data/app/views/spree/admin/orders/shipping_address/update.turbo_stream.erb +17 -0
  326. data/app/views/spree/admin/orders/user/create.turbo_stream.erb +12 -0
  327. data/app/views/spree/admin/orders/user/edit.html.erb +3 -0
  328. data/app/views/spree/admin/orders/user/new.html.erb +30 -0
  329. data/app/views/spree/admin/orders/user/update.turbo_stream.erb +5 -0
  330. data/app/views/spree/admin/page_blocks/_form_tab_buttons.html.erb +7 -0
  331. data/app/views/spree/admin/page_blocks/create.turbo_stream.erb +4 -0
  332. data/app/views/spree/admin/page_blocks/destroy.turbo_stream.erb +9 -0
  333. data/app/views/spree/admin/page_blocks/edit.html.erb +48 -0
  334. data/app/views/spree/admin/page_blocks/forms/_brand.html.erb +15 -0
  335. data/app/views/spree/admin/page_blocks/forms/_brand_logo.html.erb +4 -0
  336. data/app/views/spree/admin/page_blocks/forms/_buttons.html.erb +13 -0
  337. data/app/views/spree/admin/page_blocks/forms/_buy_buttons.html.erb +10 -0
  338. data/app/views/spree/admin/page_blocks/forms/_heading.html.erb +28 -0
  339. data/app/views/spree/admin/page_blocks/forms/_image.html.erb +1 -0
  340. data/app/views/spree/admin/page_blocks/forms/_link.html.erb +19 -0
  341. data/app/views/spree/admin/page_blocks/forms/_mega_nav.html.erb +14 -0
  342. data/app/views/spree/admin/page_blocks/forms/_mega_nav_with_subcategories.html.erb +9 -0
  343. data/app/views/spree/admin/page_blocks/forms/_nav.html.erb +6 -0
  344. data/app/views/spree/admin/page_blocks/forms/_newsletter_form.html.erb +20 -0
  345. data/app/views/spree/admin/page_blocks/forms/_price.html.erb +15 -0
  346. data/app/views/spree/admin/page_blocks/forms/_quantity_selector.html.erb +10 -0
  347. data/app/views/spree/admin/page_blocks/forms/_share.html.erb +5 -0
  348. data/app/views/spree/admin/page_blocks/forms/_subheading.html.erb +26 -0
  349. data/app/views/spree/admin/page_blocks/forms/_text.html.erb +21 -0
  350. data/app/views/spree/admin/page_blocks/forms/_title.html.erb +20 -0
  351. data/app/views/spree/admin/page_blocks/forms/_variant_picker.html.erb +10 -0
  352. data/app/views/spree/admin/page_blocks/move_higher.turbo_stream.erb +4 -0
  353. data/app/views/spree/admin/page_blocks/move_lower.turbo_stream.erb +4 -0
  354. data/app/views/spree/admin/page_blocks/show.html.erb +1 -0
  355. data/app/views/spree/admin/page_blocks/update.turbo_stream.erb +6 -0
  356. data/app/views/spree/admin/page_builder/_add_block.html.erb +20 -0
  357. data/app/views/spree/admin/page_builder/_color_palette.html.erb +17 -0
  358. data/app/views/spree/admin/page_builder/_color_picker.html.erb +26 -0
  359. data/app/views/spree/admin/page_builder/_header.html.erb +99 -0
  360. data/app/views/spree/admin/page_builder/_labeled_range_input.html.erb +10 -0
  361. data/app/views/spree/admin/page_builder/_pages_dropdown.html.erb +23 -0
  362. data/app/views/spree/admin/page_builder/_range_input.html.erb +12 -0
  363. data/app/views/spree/admin/page_builder/_sidebar.html.erb +14 -0
  364. data/app/views/spree/admin/page_builder/_sidebar_block.html.erb +29 -0
  365. data/app/views/spree/admin/page_builder/_sidebar_colors.html.erb +84 -0
  366. data/app/views/spree/admin/page_builder/_sidebar_fonts.html.erb +85 -0
  367. data/app/views/spree/admin/page_builder/_sidebar_section.html.erb +46 -0
  368. data/app/views/spree/admin/page_builder/_sidebar_sections.html.erb +33 -0
  369. data/app/views/spree/admin/page_builder/_sidebar_sections_toolbar.html.erb +65 -0
  370. data/app/views/spree/admin/page_links/_form.html.erb +19 -0
  371. data/app/views/spree/admin/page_links/_linkable_type_dropdown.html.erb +15 -0
  372. data/app/views/spree/admin/page_links/_list.html.erb +36 -0
  373. data/app/views/spree/admin/page_links/_refresh_theme_preview.turbo_stream.erb +5 -0
  374. data/app/views/spree/admin/page_links/create.turbo_stream.erb +4 -0
  375. data/app/views/spree/admin/page_links/destroy.turbo_stream.erb +11 -0
  376. data/app/views/spree/admin/page_links/edit.html.erb +18 -0
  377. data/app/views/spree/admin/page_links/update.turbo_stream.erb +6 -0
  378. data/app/views/spree/admin/page_sections/_form_tab_buttons.html.erb +12 -0
  379. data/app/views/spree/admin/page_sections/create.turbo_stream.erb +11 -0
  380. data/app/views/spree/admin/page_sections/destroy.turbo_stream.erb +16 -0
  381. data/app/views/spree/admin/page_sections/edit.html.erb +42 -0
  382. data/app/views/spree/admin/page_sections/fields/_background_color.html.erb +6 -0
  383. data/app/views/spree/admin/page_sections/fields/_border_bottom_width.html.erb +5 -0
  384. data/app/views/spree/admin/page_sections/fields/_border_color.html.erb +7 -0
  385. data/app/views/spree/admin/page_sections/fields/_border_top_width.html.erb +6 -0
  386. data/app/views/spree/admin/page_sections/fields/_bottom_padding.html.erb +5 -0
  387. data/app/views/spree/admin/page_sections/fields/_button.html.erb +15 -0
  388. data/app/views/spree/admin/page_sections/fields/_header_layout.html.erb +26 -0
  389. data/app/views/spree/admin/page_sections/fields/_height.html.erb +6 -0
  390. data/app/views/spree/admin/page_sections/fields/_text_color.html.erb +7 -0
  391. data/app/views/spree/admin/page_sections/fields/_top_padding.html.erb +6 -0
  392. data/app/views/spree/admin/page_sections/forms/_announcement_bar.html.erb +5 -0
  393. data/app/views/spree/admin/page_sections/forms/_brand_story.html.erb +0 -0
  394. data/app/views/spree/admin/page_sections/forms/_collection_banner.html.erb +0 -0
  395. data/app/views/spree/admin/page_sections/forms/_custom_code.html.erb +4 -0
  396. data/app/views/spree/admin/page_sections/forms/_featured_posts.html.erb +38 -0
  397. data/app/views/spree/admin/page_sections/forms/_featured_product.html.erb +4 -0
  398. data/app/views/spree/admin/page_sections/forms/_featured_taxon.html.erb +59 -0
  399. data/app/views/spree/admin/page_sections/forms/_featured_taxons.html.erb +13 -0
  400. data/app/views/spree/admin/page_sections/forms/_footer.html.erb +20 -0
  401. data/app/views/spree/admin/page_sections/forms/_header.html.erb +14 -0
  402. data/app/views/spree/admin/page_sections/forms/_image_banner.html.erb +13 -0
  403. data/app/views/spree/admin/page_sections/forms/_image_with_text.html.erb +31 -0
  404. data/app/views/spree/admin/page_sections/forms/_main_password_footer.html.erb +9 -0
  405. data/app/views/spree/admin/page_sections/forms/_main_password_header.erb +13 -0
  406. data/app/views/spree/admin/page_sections/forms/_newsletter.html.erb +15 -0
  407. data/app/views/spree/admin/page_sections/forms/_page_title.html.erb +4 -0
  408. data/app/views/spree/admin/page_sections/forms/_post_details.html.erb +0 -0
  409. data/app/views/spree/admin/page_sections/forms/_post_grid.html.erb +0 -0
  410. data/app/views/spree/admin/page_sections/forms/_product_details.html.erb +1 -0
  411. data/app/views/spree/admin/page_sections/forms/_product_grid.html.erb +1 -0
  412. data/app/views/spree/admin/page_sections/forms/_related_products.html.erb +32 -0
  413. data/app/views/spree/admin/page_sections/forms/_rich_text.html.erb +0 -0
  414. data/app/views/spree/admin/page_sections/forms/_taxon_banner.html.erb +0 -0
  415. data/app/views/spree/admin/page_sections/forms/_taxon_grid.html.erb +5 -0
  416. data/app/views/spree/admin/page_sections/forms/_video.html.erb +15 -0
  417. data/app/views/spree/admin/page_sections/move_higher.turbo_stream.erb +7 -0
  418. data/app/views/spree/admin/page_sections/move_lower.turbo_stream.erb +7 -0
  419. data/app/views/spree/admin/page_sections/new.html.erb +28 -0
  420. data/app/views/spree/admin/page_sections/remove_attachment.turbo_stream.erb +4 -0
  421. data/app/views/spree/admin/page_sections/restore_design_settings_to_defaults.turbo_stream.erb +4 -0
  422. data/app/views/spree/admin/page_sections/show.html.erb +1 -0
  423. data/app/views/spree/admin/page_sections/update.turbo_stream.erb +10 -0
  424. data/app/views/spree/admin/pages/_filters.html.erb +7 -0
  425. data/app/views/spree/admin/pages/_form.html.erb +29 -0
  426. data/app/views/spree/admin/pages/_page.erb +8 -0
  427. data/app/views/spree/admin/pages/edit.html.erb +18 -0
  428. data/app/views/spree/admin/pages/index.html.erb +28 -0
  429. data/app/views/spree/admin/pages/new.html.erb +11 -0
  430. data/app/views/spree/admin/payment_methods/_available_payment_method.html.erb +21 -0
  431. data/app/views/spree/admin/payment_methods/_check.html.erb +0 -0
  432. data/app/views/spree/admin/payment_methods/_form.html.erb +71 -0
  433. data/app/views/spree/admin/payment_methods/_payment_method.html.erb +38 -0
  434. data/app/views/spree/admin/payment_methods/descriptions/_check.html.erb +0 -0
  435. data/app/views/spree/admin/payment_methods/descriptions/_gateway.html.erb +1 -0
  436. data/app/views/spree/admin/payment_methods/descriptions/_store_credit.html.erb +1 -0
  437. data/app/views/spree/admin/payment_methods/edit.html.erb +10 -0
  438. data/app/views/spree/admin/payment_methods/index.html.erb +38 -0
  439. data/app/views/spree/admin/payment_methods/new.html.erb +12 -0
  440. data/app/views/spree/admin/payment_methods/update.turbo_stream.erb +1 -0
  441. data/app/views/spree/admin/payments/_payment.html.erb +89 -0
  442. data/app/views/spree/admin/payments/new.html.erb +64 -0
  443. data/app/views/spree/admin/payments/source_forms/_gateway.html.erb +54 -0
  444. data/app/views/spree/admin/payments/source_forms/_previous_cards.html.erb +21 -0
  445. data/app/views/spree/admin/payments/source_forms/_spree_stripe.html.erb +7 -0
  446. data/app/views/spree/admin/payments/source_forms/_store_credit.html.erb +22 -0
  447. data/app/views/spree/admin/payments/source_views/_check.html.erb +0 -0
  448. data/app/views/spree/admin/payments/source_views/_gateway.html.erb +22 -0
  449. data/app/views/spree/admin/payments/source_views/_storecredit.html.erb +31 -0
  450. data/app/views/spree/admin/post_categories/_form.html.erb +15 -0
  451. data/app/views/spree/admin/post_categories/_post_category.html.erb +6 -0
  452. data/app/views/spree/admin/post_categories/edit.html.erb +15 -0
  453. data/app/views/spree/admin/post_categories/index.html.erb +27 -0
  454. data/app/views/spree/admin/post_categories/new.html.erb +15 -0
  455. data/app/views/spree/admin/posts/_extra_actions.html.erb +0 -0
  456. data/app/views/spree/admin/posts/_filters.html.erb +40 -0
  457. data/app/views/spree/admin/posts/_form.html.erb +81 -0
  458. data/app/views/spree/admin/posts/_post.html.erb +32 -0
  459. data/app/views/spree/admin/posts/edit.html.erb +15 -0
  460. data/app/views/spree/admin/posts/index.html.erb +36 -0
  461. data/app/views/spree/admin/posts/new.html.erb +11 -0
  462. data/app/views/spree/admin/preferences/_password_field.html.erb +26 -0
  463. data/app/views/spree/admin/products/_bulk_operations.html.erb +122 -0
  464. data/app/views/spree/admin/products/_edit_page_title.html.erb +3 -0
  465. data/app/views/spree/admin/products/_empty_list.html.erb +1 -0
  466. data/app/views/spree/admin/products/_filters.html.erb +60 -0
  467. data/app/views/spree/admin/products/_form.html.erb +29 -0
  468. data/app/views/spree/admin/products/_import_button.html.erb +0 -0
  469. data/app/views/spree/admin/products/_index_header.html.erb +7 -0
  470. data/app/views/spree/admin/products/_list.html.erb +50 -0
  471. data/app/views/spree/admin/products/_product.html.erb +30 -0
  472. data/app/views/spree/admin/products/_product_status.html.erb +3 -0
  473. data/app/views/spree/admin/products/_search_result.html.erb +9 -0
  474. data/app/views/spree/admin/products/_search_results.html.erb +9 -0
  475. data/app/views/spree/admin/products/_table_filter_dropdown.html.erb +42 -0
  476. data/app/views/spree/admin/products/bulk_modal.html.erb +23 -0
  477. data/app/views/spree/admin/products/bulk_status_update.turbo_stream.erb +7 -0
  478. data/app/views/spree/admin/products/edit.html.erb +38 -0
  479. data/app/views/spree/admin/products/form/_base.html.erb +16 -0
  480. data/app/views/spree/admin/products/form/_categorization.html.erb +35 -0
  481. data/app/views/spree/admin/products/form/_extra_filters.html.erb +0 -0
  482. data/app/views/spree/admin/products/form/_extra_form.html.erb +0 -0
  483. data/app/views/spree/admin/products/form/_extra_form_sidebar.erb +0 -0
  484. data/app/views/spree/admin/products/form/_inventory.html.erb +63 -0
  485. data/app/views/spree/admin/products/form/_properties.html.erb +46 -0
  486. data/app/views/spree/admin/products/form/_shipping.html.erb +16 -0
  487. data/app/views/spree/admin/products/form/_status.html.erb +52 -0
  488. data/app/views/spree/admin/products/form/_stores.html.erb +27 -0
  489. data/app/views/spree/admin/products/form/_variants.html.erb +171 -0
  490. data/app/views/spree/admin/products/form/variants/_option_template.html.erb +59 -0
  491. data/app/views/spree/admin/products/form/variants/_variant_template.html.erb +44 -0
  492. data/app/views/spree/admin/products/index.html.erb +18 -0
  493. data/app/views/spree/admin/products/new.html.erb +9 -0
  494. data/app/views/spree/admin/profile/edit.html.erb +79 -0
  495. data/app/views/spree/admin/promotion_actions/_promotion_action.html.erb +45 -0
  496. data/app/views/spree/admin/promotion_actions/edit.html.erb +17 -0
  497. data/app/views/spree/admin/promotion_actions/forms/_create_adjustment.html.erb +1 -0
  498. data/app/views/spree/admin/promotion_actions/forms/_create_item_adjustments.html.erb +1 -0
  499. data/app/views/spree/admin/promotion_actions/forms/_create_line_items.html.erb +35 -0
  500. data/app/views/spree/admin/promotion_actions/forms/_create_line_items_row.html.erb +28 -0
  501. data/app/views/spree/admin/promotion_actions/forms/_free_shipping.html.erb +0 -0
  502. data/app/views/spree/admin/promotion_actions/new.html.erb +48 -0
  503. data/app/views/spree/admin/promotion_rules/_promotion_rule.html.erb +53 -0
  504. data/app/views/spree/admin/promotion_rules/edit.html.erb +17 -0
  505. data/app/views/spree/admin/promotion_rules/forms/_country.html.erb +7 -0
  506. data/app/views/spree/admin/promotion_rules/forms/_currency.html.erb +10 -0
  507. data/app/views/spree/admin/promotion_rules/forms/_first_order.html.erb +0 -0
  508. data/app/views/spree/admin/promotion_rules/forms/_item_total.html.erb +26 -0
  509. data/app/views/spree/admin/promotion_rules/forms/_one_use_per_user.html.erb +0 -0
  510. data/app/views/spree/admin/promotion_rules/forms/_option_value.html.erb +9 -0
  511. data/app/views/spree/admin/promotion_rules/forms/_product.html.erb +9 -0
  512. data/app/views/spree/admin/promotion_rules/forms/_taxon.html.erb +9 -0
  513. data/app/views/spree/admin/promotion_rules/forms/_user.html.erb +7 -0
  514. data/app/views/spree/admin/promotion_rules/forms/_user_logged_in.html.erb +0 -0
  515. data/app/views/spree/admin/promotion_rules/new.html.erb +48 -0
  516. data/app/views/spree/admin/promotions/_actions.html.erb +30 -0
  517. data/app/views/spree/admin/promotions/_form.html.erb +47 -0
  518. data/app/views/spree/admin/promotions/_header.html.erb +16 -0
  519. data/app/views/spree/admin/promotions/_promotion.html.erb +34 -0
  520. data/app/views/spree/admin/promotions/_rules.html.erb +44 -0
  521. data/app/views/spree/admin/promotions/_sidebar.html.erb +117 -0
  522. data/app/views/spree/admin/promotions/_status.html.erb +16 -0
  523. data/app/views/spree/admin/promotions/_table_filter_dropdown.html.erb +17 -0
  524. data/app/views/spree/admin/promotions/_usage_limit.html.erb +13 -0
  525. data/app/views/spree/admin/promotions/edit.html.erb +59 -0
  526. data/app/views/spree/admin/promotions/form/_kind.html.erb +57 -0
  527. data/app/views/spree/admin/promotions/form/_settings.html.erb +24 -0
  528. data/app/views/spree/admin/promotions/index.html.erb +62 -0
  529. data/app/views/spree/admin/promotions/new.html.erb +11 -0
  530. data/app/views/spree/admin/promotions/show.html.erb +12 -0
  531. data/app/views/spree/admin/properties/_form.html.erb +22 -0
  532. data/app/views/spree/admin/properties/_property.html.erb +25 -0
  533. data/app/views/spree/admin/properties/edit.html.erb +15 -0
  534. data/app/views/spree/admin/properties/index.html.erb +53 -0
  535. data/app/views/spree/admin/properties/new.html.erb +14 -0
  536. data/app/views/spree/admin/properties/update.turbo_stream.erb +1 -0
  537. data/app/views/spree/admin/refund_reasons/edit.html.erb +3 -0
  538. data/app/views/spree/admin/refund_reasons/index.html.erb +40 -0
  539. data/app/views/spree/admin/refund_reasons/new.html.erb +4 -0
  540. data/app/views/spree/admin/refunds/_form.html.erb +17 -0
  541. data/app/views/spree/admin/refunds/edit.html.erb +20 -0
  542. data/app/views/spree/admin/refunds/new.html.erb +29 -0
  543. data/app/views/spree/admin/reimbursement_types/_form.html.erb +30 -0
  544. data/app/views/spree/admin/reimbursement_types/edit.html.erb +15 -0
  545. data/app/views/spree/admin/reimbursement_types/index.html.erb +44 -0
  546. data/app/views/spree/admin/reimbursement_types/new.html.erb +16 -0
  547. data/app/views/spree/admin/reimbursements/edit.html.erb +130 -0
  548. data/app/views/spree/admin/reimbursements/show.html.erb +113 -0
  549. data/app/views/spree/admin/reports/_report.html.erb +22 -0
  550. data/app/views/spree/admin/reports/create.turbo_stream.erb +1 -0
  551. data/app/views/spree/admin/reports/index.html.erb +28 -0
  552. data/app/views/spree/admin/reports/new.html.erb +39 -0
  553. data/app/views/spree/admin/return_authorization_reasons/edit.html.erb +3 -0
  554. data/app/views/spree/admin/return_authorization_reasons/index.html.erb +45 -0
  555. data/app/views/spree/admin/return_authorization_reasons/new.html.erb +4 -0
  556. data/app/views/spree/admin/return_authorizations/_filters.html.erb +39 -0
  557. data/app/views/spree/admin/return_authorizations/_list.html.erb +30 -0
  558. data/app/views/spree/admin/return_authorizations/_return_authorization.html.erb +40 -0
  559. data/app/views/spree/admin/return_authorizations/index.html.erb +14 -0
  560. data/app/views/spree/admin/roles/_form.html.erb +6 -0
  561. data/app/views/spree/admin/roles/edit.html.erb +13 -0
  562. data/app/views/spree/admin/roles/index.html.erb +32 -0
  563. data/app/views/spree/admin/roles/new.html.erb +15 -0
  564. data/app/views/spree/admin/shared/_address.html.erb +42 -0
  565. data/app/views/spree/admin/shared/_alerts.html.erb +22 -0
  566. data/app/views/spree/admin/shared/_analytics_loader_skeleton.html.erb +76 -0
  567. data/app/views/spree/admin/shared/_audit_nav.html.erb +3 -0
  568. data/app/views/spree/admin/shared/_bulk_modal.html.erb +21 -0
  569. data/app/views/spree/admin/shared/_calculator_fields.html.erb +34 -0
  570. data/app/views/spree/admin/shared/_calendar_range_picker.html.erb +28 -0
  571. data/app/views/spree/admin/shared/_close_bulk_modal.turbo_stream.erb +2 -0
  572. data/app/views/spree/admin/shared/_content_header.html.erb +81 -0
  573. data/app/views/spree/admin/shared/_custom_head.html.erb +0 -0
  574. data/app/views/spree/admin/shared/_developers_nav.html.erb +7 -0
  575. data/app/views/spree/admin/shared/_edit_resource_links.html.erb +6 -0
  576. data/app/views/spree/admin/shared/_error_messages.html.erb +22 -0
  577. data/app/views/spree/admin/shared/_filter_badge_template.erb +3 -0
  578. data/app/views/spree/admin/shared/_filters_button.html.erb +8 -0
  579. data/app/views/spree/admin/shared/_filters_search_bar.html.erb +15 -0
  580. data/app/views/spree/admin/shared/_footer.html.erb +2 -0
  581. data/app/views/spree/admin/shared/_growth_rate_badge.html.erb +17 -0
  582. data/app/views/spree/admin/shared/_head.html.erb +52 -0
  583. data/app/views/spree/admin/shared/_header.html.erb +28 -0
  584. data/app/views/spree/admin/shared/_index_table_options.html.erb +22 -0
  585. data/app/views/spree/admin/shared/_map.html.erb +9 -0
  586. data/app/views/spree/admin/shared/_modal.html.erb +37 -0
  587. data/app/views/spree/admin/shared/_multi_product_picker.html.erb +26 -0
  588. data/app/views/spree/admin/shared/_new_resource_links.html.erb +3 -0
  589. data/app/views/spree/admin/shared/_no_image.html.erb +6 -0
  590. data/app/views/spree/admin/shared/_no_resource_found.html.erb +6 -0
  591. data/app/views/spree/admin/shared/_offcanvas_nav.html.erb +25 -0
  592. data/app/views/spree/admin/shared/_page_section_image.html.erb +1 -0
  593. data/app/views/spree/admin/shared/_page_section_logo.html.erb +1 -0
  594. data/app/views/spree/admin/shared/_posts_tabs.html.erb +8 -0
  595. data/app/views/spree/admin/shared/_preferences.html.erb +9 -0
  596. data/app/views/spree/admin/shared/_product_image.html.erb +21 -0
  597. data/app/views/spree/admin/shared/_refunds.html.erb +30 -0
  598. data/app/views/spree/admin/shared/_returns_and_refunds_nav.html.erb +9 -0
  599. data/app/views/spree/admin/shared/_seo.html.erb +39 -0
  600. data/app/views/spree/admin/shared/_shipping_nav.html.erb +4 -0
  601. data/app/views/spree/admin/shared/_sidebar.html.erb +15 -0
  602. data/app/views/spree/admin/shared/_spinner.html.erb +7 -0
  603. data/app/views/spree/admin/shared/_stock_nav.html.erb +4 -0
  604. data/app/views/spree/admin/shared/_tax_nav.html.erb +4 -0
  605. data/app/views/spree/admin/shared/_user.html.erb +24 -0
  606. data/app/views/spree/admin/shared/_user_dropdown.html.erb +41 -0
  607. data/app/views/spree/admin/shared/named_types/_edit.html.erb +15 -0
  608. data/app/views/spree/admin/shared/named_types/_form.html.erb +15 -0
  609. data/app/views/spree/admin/shared/named_types/_new.html.erb +15 -0
  610. data/app/views/spree/admin/shared/sidebar/_custom_nav.html.erb +0 -0
  611. data/app/views/spree/admin/shared/sidebar/_enterprise_edition_notice.html.erb +10 -0
  612. data/app/views/spree/admin/shared/sidebar/_integrations_nav.html.erb +7 -0
  613. data/app/views/spree/admin/shared/sidebar/_orders_nav.html.erb +35 -0
  614. data/app/views/spree/admin/shared/sidebar/_products_nav.html.erb +41 -0
  615. data/app/views/spree/admin/shared/sidebar/_returns_nav.html.erb +20 -0
  616. data/app/views/spree/admin/shared/sidebar/_store_dropdown.html.erb +67 -0
  617. data/app/views/spree/admin/shared/sidebar/_store_nav.html.erb +176 -0
  618. data/app/views/spree/admin/shared/sidebar/_storefront_nav.html.erb +29 -0
  619. data/app/views/spree/admin/shared/sidebar/_vendors_nav.html.erb +8 -0
  620. data/app/views/spree/admin/shared/sortable_tree/_taxonomy.html.erb +66 -0
  621. data/app/views/spree/admin/shipments/edit.html.erb +28 -0
  622. data/app/views/spree/admin/shipments/split.html.erb +16 -0
  623. data/app/views/spree/admin/shipping_categories/_form.html.erb +9 -0
  624. data/app/views/spree/admin/shipping_categories/edit.html.erb +11 -0
  625. data/app/views/spree/admin/shipping_categories/index.html.erb +36 -0
  626. data/app/views/spree/admin/shipping_categories/new.html.erb +13 -0
  627. data/app/views/spree/admin/shipping_methods/_actions.html.erb +5 -0
  628. data/app/views/spree/admin/shipping_methods/_form.html.erb +141 -0
  629. data/app/views/spree/admin/shipping_methods/_shipping_method.html.erb +33 -0
  630. data/app/views/spree/admin/shipping_methods/edit.html.erb +12 -0
  631. data/app/views/spree/admin/shipping_methods/index.html.erb +34 -0
  632. data/app/views/spree/admin/shipping_methods/new.html.erb +13 -0
  633. data/app/views/spree/admin/shipping_methods/update.turbo_stream.erb +1 -0
  634. data/app/views/spree/admin/stock_items/_filters.html.erb +39 -0
  635. data/app/views/spree/admin/stock_items/_stock_item.html.erb +43 -0
  636. data/app/views/spree/admin/stock_items/index.html.erb +38 -0
  637. data/app/views/spree/admin/stock_items/update.turbo_stream.erb +1 -0
  638. data/app/views/spree/admin/stock_locations/_form.html.erb +56 -0
  639. data/app/views/spree/admin/stock_locations/_stock_location.html.erb +30 -0
  640. data/app/views/spree/admin/stock_locations/edit.html.erb +9 -0
  641. data/app/views/spree/admin/stock_locations/index.html.erb +26 -0
  642. data/app/views/spree/admin/stock_locations/new.html.erb +11 -0
  643. data/app/views/spree/admin/stock_transfers/_destination_movement_form.html.erb +36 -0
  644. data/app/views/spree/admin/stock_transfers/_filters.html.erb +46 -0
  645. data/app/views/spree/admin/stock_transfers/_new_variant_modal.html.erb +30 -0
  646. data/app/views/spree/admin/stock_transfers/_new_variant_template.html.erb +8 -0
  647. data/app/views/spree/admin/stock_transfers/_source_movement_form.html.erb +8 -0
  648. data/app/views/spree/admin/stock_transfers/_stock_movement.html.erb +5 -0
  649. data/app/views/spree/admin/stock_transfers/_stock_transfer.html.erb +11 -0
  650. data/app/views/spree/admin/stock_transfers/index.html.erb +38 -0
  651. data/app/views/spree/admin/stock_transfers/new.html.erb +85 -0
  652. data/app/views/spree/admin/stock_transfers/show.html.erb +58 -0
  653. data/app/views/spree/admin/store_credit_categories/_form.html.erb +5 -0
  654. data/app/views/spree/admin/store_credit_categories/edit.html.erb +13 -0
  655. data/app/views/spree/admin/store_credit_categories/index.html.erb +32 -0
  656. data/app/views/spree/admin/store_credit_categories/new.html.erb +15 -0
  657. data/app/views/spree/admin/store_credits/_form.html.erb +24 -0
  658. data/app/views/spree/admin/store_credits/_list.html.erb +41 -0
  659. data/app/views/spree/admin/store_credits/edit.html.erb +36 -0
  660. data/app/views/spree/admin/store_credits/index.html.erb +20 -0
  661. data/app/views/spree/admin/store_credits/new.html.erb +26 -0
  662. data/app/views/spree/admin/storefront/edit.html.erb +146 -0
  663. data/app/views/spree/admin/stores/create.turbo_stream.erb +5 -0
  664. data/app/views/spree/admin/stores/edit.html.erb +13 -0
  665. data/app/views/spree/admin/stores/edit_emails.html.erb +11 -0
  666. data/app/views/spree/admin/stores/form/_basic.html.erb +160 -0
  667. data/app/views/spree/admin/stores/form/_buttons.html.erb +3 -0
  668. data/app/views/spree/admin/stores/form/_checkout.html.erb +50 -0
  669. data/app/views/spree/admin/stores/form/_custom.html.erb +0 -0
  670. data/app/views/spree/admin/stores/form/_emails.html.erb +59 -0
  671. data/app/views/spree/admin/stores/form/_policies.html.erb +85 -0
  672. data/app/views/spree/admin/stores/new.html.erb +135 -0
  673. data/app/views/spree/admin/stores/new.turbo_stream.erb +1 -0
  674. data/app/views/spree/admin/stores/update.turbo_stream.erb +11 -0
  675. data/app/views/spree/admin/tax_categories/_form.html.erb +25 -0
  676. data/app/views/spree/admin/tax_categories/_tax_category.html.erb +13 -0
  677. data/app/views/spree/admin/tax_categories/edit.html.erb +11 -0
  678. data/app/views/spree/admin/tax_categories/index.html.erb +30 -0
  679. data/app/views/spree/admin/tax_categories/new.html.erb +11 -0
  680. data/app/views/spree/admin/tax_rates/_form.html.erb +48 -0
  681. data/app/views/spree/admin/tax_rates/_tax_rate.html.erb +15 -0
  682. data/app/views/spree/admin/tax_rates/edit.html.erb +11 -0
  683. data/app/views/spree/admin/tax_rates/index.html.erb +32 -0
  684. data/app/views/spree/admin/tax_rates/new.html.erb +14 -0
  685. data/app/views/spree/admin/taxonomies/_taxonomy.html.erb +16 -0
  686. data/app/views/spree/admin/taxonomies/edit.html.erb +28 -0
  687. data/app/views/spree/admin/taxonomies/index.html.erb +40 -0
  688. data/app/views/spree/admin/taxonomies/new.html.erb +23 -0
  689. data/app/views/spree/admin/taxonomies/show.html.erb +28 -0
  690. data/app/views/spree/admin/taxons/_form.html.erb +148 -0
  691. data/app/views/spree/admin/taxons/_rule_form.html.erb +24 -0
  692. data/app/views/spree/admin/taxons/_rules_form.html.erb +34 -0
  693. data/app/views/spree/admin/taxons/destroy.turbo_stream.erb +1 -0
  694. data/app/views/spree/admin/taxons/edit.html.erb +19 -0
  695. data/app/views/spree/admin/taxons/new.html.erb +12 -0
  696. data/app/views/spree/admin/taxons/rule_forms/_available_on.html.erb +4 -0
  697. data/app/views/spree/admin/taxons/rule_forms/_sale.html.erb +4 -0
  698. data/app/views/spree/admin/taxons/rule_forms/_tag.html.erb +1 -0
  699. data/app/views/spree/admin/themes/_theme.html.erb +53 -0
  700. data/app/views/spree/admin/themes/_theme_preview_image.html.erb +24 -0
  701. data/app/views/spree/admin/themes/edit.html.erb +14 -0
  702. data/app/views/spree/admin/themes/index.html.erb +79 -0
  703. data/app/views/spree/admin/themes/update.turbo_stream.erb +1 -0
  704. data/app/views/spree/admin/translations/_translations_unavailable.html.erb +10 -0
  705. data/app/views/spree/admin/translations/edit.html.erb +47 -0
  706. data/app/views/spree/admin/translations/products/_form.html.erb +23 -0
  707. data/app/views/spree/admin/translations/stores/_form.html.erb +23 -0
  708. data/app/views/spree/admin/translations/taxonomies/_form.html.erb +20 -0
  709. data/app/views/spree/admin/translations/taxons/_form.html.erb +37 -0
  710. data/app/views/spree/admin/users/_billing.html.erb +78 -0
  711. data/app/views/spree/admin/users/_details.html.erb +52 -0
  712. data/app/views/spree/admin/users/_extra_actions.html.erb +0 -0
  713. data/app/views/spree/admin/users/_filters.html.erb +85 -0
  714. data/app/views/spree/admin/users/_form.html.erb +36 -0
  715. data/app/views/spree/admin/users/_last_order.html.erb +33 -0
  716. data/app/views/spree/admin/users/_last_order_line_item.html.erb +19 -0
  717. data/app/views/spree/admin/users/_lifetime_stats.html.erb +65 -0
  718. data/app/views/spree/admin/users/_shipping.html.erb +33 -0
  719. data/app/views/spree/admin/users/_tabs.html.erb +74 -0
  720. data/app/views/spree/admin/users/_user.html.erb +20 -0
  721. data/app/views/spree/admin/users/bulk_modal.html.erb +21 -0
  722. data/app/views/spree/admin/users/edit.html.erb +17 -0
  723. data/app/views/spree/admin/users/index.csv.erb +43 -0
  724. data/app/views/spree/admin/users/index.html.erb +102 -0
  725. data/app/views/spree/admin/users/new.html.erb +14 -0
  726. data/app/views/spree/admin/users/show.html.erb +38 -0
  727. data/app/views/spree/admin/variants/_search_result.html.erb +28 -0
  728. data/app/views/spree/admin/variants/_search_results.html.erb +9 -0
  729. data/app/views/spree/admin/variants/_variant.html.erb +13 -0
  730. data/app/views/spree/admin/variants/edit.html.erb +20 -0
  731. data/app/views/spree/admin/variants/form/_basic.html.erb +31 -0
  732. data/app/views/spree/admin/variants/form/_dimensions.html.erb +33 -0
  733. data/app/views/spree/admin/variants/form/_inventory.html.erb +45 -0
  734. data/app/views/spree/admin/variants/form/_media.html.erb +32 -0
  735. data/app/views/spree/admin/variants/form/_media_asset.html.erb +21 -0
  736. data/app/views/spree/admin/variants/form/_pricing.html.erb +48 -0
  737. data/app/views/spree/admin/webhook_events/_webhook_event.html.erb +17 -0
  738. data/app/views/spree/admin/webhooks_subscribers/_form.html.erb +66 -0
  739. data/app/views/spree/admin/webhooks_subscribers/_webhooks_subscriber.html.erb +16 -0
  740. data/app/views/spree/admin/webhooks_subscribers/edit.html.erb +9 -0
  741. data/app/views/spree/admin/webhooks_subscribers/index.html.erb +26 -0
  742. data/app/views/spree/admin/webhooks_subscribers/new.html.erb +11 -0
  743. data/app/views/spree/admin/webhooks_subscribers/show.html.erb +54 -0
  744. data/app/views/spree/admin/zones/_country_members.html.erb +16 -0
  745. data/app/views/spree/admin/zones/_form.html.erb +52 -0
  746. data/app/views/spree/admin/zones/_state_members.html.erb +21 -0
  747. data/app/views/spree/admin/zones/edit.html.erb +12 -0
  748. data/app/views/spree/admin/zones/index.html.erb +45 -0
  749. data/app/views/spree/admin/zones/new.html.erb +11 -0
  750. data/config/i18n-tasks.yml +190 -0
  751. data/config/importmap.rb +149 -0
  752. data/config/initializers/action_text.rb +8 -0
  753. data/config/initializers/assets.rb +1 -0
  754. data/config/initializers/chartkick.rb +5 -0
  755. data/config/initializers/dartsass.rb +3 -0
  756. data/config/initializers/form_builder.rb +10 -0
  757. data/config/initializers/ransack.rb +19 -0
  758. data/config/locales/en.yml +304 -0
  759. data/config/routes.rb +240 -0
  760. data/lib/generators/spree/admin/install/install_generator.rb +42 -0
  761. data/lib/generators/spree/admin/install/templates/dev +16 -0
  762. data/lib/spree/admin/action_callbacks.rb +27 -0
  763. data/lib/spree/admin/callbacks.rb +51 -0
  764. data/lib/spree/admin/engine.rb +51 -0
  765. data/lib/spree/admin/runtime_configuration.rb +12 -0
  766. data/lib/spree/admin/testing_support/capybara_utils.rb +45 -0
  767. data/lib/spree/admin.rb +23 -0
  768. data/lib/spree_admin.rb +18 -0
  769. data/vendor/javascript/@easepick--base-plugin.js +4 -0
  770. data/vendor/javascript/@easepick--core.js +4 -0
  771. data/vendor/javascript/@easepick--datetime.js +4 -0
  772. data/vendor/javascript/@easepick--preset-plugin.js +4 -0
  773. data/vendor/javascript/@easepick--range-plugin.js +4 -0
  774. data/vendor/javascript/@orchidjs--sifter.js +17 -0
  775. data/vendor/javascript/@orchidjs--unicode-variants.js +11 -0
  776. data/vendor/javascript/@rails--actiontext@7.2.201.js +4 -0
  777. data/vendor/javascript/@simonwep--pickr.js +4 -0
  778. data/vendor/javascript/@stimulus-components--rails-nested-form.js +4 -0
  779. data/vendor/javascript/bootstrap--dist--js--bootstrap.bundle.min.js.js +8 -0
  780. data/vendor/javascript/dompurify.js +139 -0
  781. data/vendor/javascript/hotkeys-js.js +4 -0
  782. data/vendor/javascript/jquery.min.js +2 -0
  783. data/vendor/javascript/local-time.js +4 -0
  784. data/vendor/javascript/sortablejs.js +136 -0
  785. data/vendor/javascript/stimulus-checkbox-select-all.js +4 -0
  786. data/vendor/javascript/stimulus-clipboard.js +4 -0
  787. data/vendor/javascript/stimulus-notification.js +4 -0
  788. data/vendor/javascript/stimulus-password-visibility.js +4 -0
  789. data/vendor/javascript/stimulus-reveal-controller.js +4 -0
  790. data/vendor/javascript/stimulus-sortable.js +4 -0
  791. data/vendor/javascript/stimulus-textarea-autogrow.js +4 -0
  792. data/vendor/javascript/stimulus-use.js +4 -0
  793. data/vendor/javascript/tom-select--dist--esm--tom-select.complete.js.js +32 -0
  794. data/vendor/javascript/trix@2.1.12.js +5 -0
  795. metadata +1084 -0
@@ -0,0 +1,1048 @@
1
+ import CheckboxSelectAll from 'stimulus-checkbox-select-all'
2
+ import { Sortable } from 'sortablejs'
3
+ import { get } from '@rails/request.js'
4
+
5
+ export default class extends CheckboxSelectAll {
6
+ static targets = [
7
+ 'optionTemplate',
8
+ 'optionValueTemplate',
9
+ 'optionsContainer',
10
+ 'optionFormTemplate',
11
+ 'newOptionForm',
12
+ 'newOptionValuesSelectContainer',
13
+ 'newOptionValuesSelect',
14
+ 'newOptionNameInput',
15
+ 'newOptionButton',
16
+ 'newOptionButtonLabel',
17
+ 'option',
18
+ 'variantsContainer',
19
+ 'variantTemplate',
20
+ 'variantsTable',
21
+ 'deleteButton',
22
+ 'checkboxAll',
23
+ 'checkbox',
24
+ 'stockItemsCount'
25
+ ]
26
+
27
+ static values = {
28
+ productId: String,
29
+ options: Object,
30
+ availableOptions: Object,
31
+ variants: Array,
32
+ stock: Object,
33
+ prices: Object,
34
+ currentCurrency: String,
35
+ currencies: Array,
36
+ variantIds: Object,
37
+ currentStockLocationId: String,
38
+ stockLocations: Array,
39
+ optionValuesSelectOptions: Array
40
+ }
41
+
42
+ connect() {
43
+ super.connect()
44
+ this.optionNameOptions = this.newOptionNameInputTarget.options
45
+ this.sortable = new Sortable(this.optionsContainerTarget, {
46
+ group: 'options',
47
+ animation: 150,
48
+ onEnd: this.reorderOptions.bind(this),
49
+ handle: '.draggable',
50
+ filter: '.color-option',
51
+ draggable: '.options-creator__option'
52
+ })
53
+ this.ignoredVariants = new Set()
54
+ this.productFormController = this.application.getControllerForElementAndIdentifier(
55
+ this.element.closest('[data-controller*="product-form"]'),
56
+ 'product-form'
57
+ )
58
+ this.currentOptionValues = {}
59
+
60
+ // Set ignoredVariants to all the variants that are not on the server
61
+ const existingVariantsOnServer = Object.keys(this.variantIdsValue)
62
+ if (existingVariantsOnServer.length > 0) {
63
+ this.ignoredVariants = new Set(
64
+ this.variantsValue
65
+ .map((variant) => variant.internalName)
66
+ .filter((internalName) => !existingVariantsOnServer.includes(internalName))
67
+ )
68
+ }
69
+ }
70
+
71
+ toggleQuantityTracked() {
72
+ this.element.querySelectorAll('.column-quantity').forEach((el) => el.classList.toggle('d-none'))
73
+
74
+ this.variantTemplateTarget.content
75
+ .querySelectorAll('.column-quantity')
76
+ .forEach((el) => el.classList.toggle('d-none'))
77
+ }
78
+
79
+ toggle(e) {
80
+ super.toggle(e)
81
+ this.toggleDeleteButton()
82
+ }
83
+
84
+ refresh() {
85
+ super.refresh()
86
+
87
+ // Set indeterminate state for parent checkboxes
88
+ this.element.querySelectorAll('input[id^="parent_checkbox_"]').forEach((checkbox) => {
89
+ checkbox.indeterminate = false
90
+ })
91
+
92
+ // Set checked state for parent checkboxes based on the amount of checked children
93
+ const firstOptionKeys = new Set(this.checked.map((c) => c.value.split('/')[0]))
94
+ firstOptionKeys.forEach((key) => {
95
+ const parentCheckbox = this.element.querySelector(`input[id="parent_checkbox_${key}"]`)
96
+ if (!parentCheckbox) return
97
+
98
+ const childCheckboxes = Array.from(this.element.querySelectorAll(`input[value^="${key}/"]`))
99
+ const allChildrenChecked = childCheckboxes.length > 0 && childCheckboxes.every((c) => c.checked)
100
+
101
+ parentCheckbox.checked = allChildrenChecked
102
+ parentCheckbox.indeterminate = !allChildrenChecked && childCheckboxes.some((c) => c.checked)
103
+ })
104
+
105
+ this.toggleDeleteButton()
106
+ }
107
+
108
+ toggleDeleteButton() {
109
+ if (this.checked.length > 0) {
110
+ this.deleteButtonTarget.classList.remove('d-none')
111
+ } else {
112
+ this.deleteButtonTarget.classList.add('d-none')
113
+ }
114
+ }
115
+
116
+ deleteSelected() {
117
+ const newStockValue = this.stockValue
118
+ const newPricesValue = this.pricesValue
119
+ this.checked.forEach((checkbox) => {
120
+ const internalName = checkbox.value
121
+
122
+ this.ignoredVariants.add(internalName)
123
+ const variant = this.variantsContainerTarget.querySelector(`[data-variant-name="${internalName}"]`)
124
+
125
+ const nestingLevel = internalName.split('/').length
126
+ if (nestingLevel === 1) {
127
+ const firstOptionKey = Object.keys(this.optionsValue)[0]
128
+ const newOptionValues = this.optionsValue[firstOptionKey].values.filter((value) => value !== internalName)
129
+ if (newOptionValues.length === 0) {
130
+ const newOptionsValue = this.optionsValue
131
+ delete newOptionsValue[firstOptionKey]
132
+ this.optionsValue = newOptionsValue
133
+ this.optionsContainerTarget.querySelector(`#option-${firstOptionKey}`).remove()
134
+ } else {
135
+ this.optionsValue = {
136
+ ...this.optionsValue,
137
+ [firstOptionKey]: {
138
+ ...this.optionsValue[firstOptionKey],
139
+ values: newOptionValues
140
+ }
141
+ }
142
+
143
+ this.optionsContainerTarget.querySelector(`#option-${firstOptionKey} [data-name="${internalName}"]`).remove()
144
+ }
145
+ checkbox.checked = false
146
+ }
147
+
148
+ delete newStockValue[internalName]
149
+ delete newPricesValue[internalName]
150
+ variant.remove()
151
+ })
152
+
153
+ this.stockValue = newStockValue
154
+ this.pricesValue = newPricesValue
155
+
156
+ this.checkboxAllTarget.checked = false
157
+ this.refresh()
158
+ this.refreshParentInputs()
159
+ }
160
+
161
+ reorderOptions(event) {
162
+ const optionId = event.item.id.replace('option-', '')
163
+ const newPosition = event.newDraggableIndex + 1
164
+ const oldPosition = event.oldDraggableIndex + 1
165
+ const options = Object.keys(this.optionsValue).reduce((acc, key) => {
166
+ if (key === optionId) {
167
+ acc[key] = { ...this.optionsValue[key], position: newPosition }
168
+ } else if (newPosition < oldPosition) {
169
+ if (this.optionsValue[key].position >= newPosition && this.optionsValue[key].position < oldPosition) {
170
+ acc[key] = { ...this.optionsValue[key], position: this.optionsValue[key].position + 1 }
171
+ } else {
172
+ acc[key] = this.optionsValue[key]
173
+ }
174
+ } else {
175
+ if (this.optionsValue[key].position > oldPosition && this.optionsValue[key].position <= newPosition) {
176
+ acc[key] = { ...this.optionsValue[key], position: this.optionsValue[key].position - 1 }
177
+ } else {
178
+ acc[key] = this.optionsValue[key]
179
+ }
180
+ }
181
+ return acc
182
+ }, {})
183
+ this.optionsValue = options
184
+ }
185
+
186
+ updateShopLocationCountOnHand() {
187
+ const inputs = this.variantsContainerTarget.querySelectorAll(
188
+ `input[data-slot='[stock_items_attributes][${this.currentStockLocationIdValue}][count_on_hand]_input'][name$='[count_on_hand]']`
189
+ )
190
+
191
+ const sum = Array.from(inputs).reduce((acc, input) => {
192
+ if (input.value === '') return acc
193
+ return acc + parseInt(input.value)
194
+ }, 0)
195
+ this.stockItemsCountTarget.textContent = sum
196
+ }
197
+
198
+ updateStockLocationId({ target: { value: newStockLocationId } }) {
199
+ newStockLocationId = String(newStockLocationId)
200
+
201
+ this.currentStockLocationIdValue = newStockLocationId
202
+ this.stockLocationsValue.forEach((stockLocationId) => {
203
+ this.variantsContainerTarget
204
+ .querySelectorAll(`input[data-slot='[stock_items_attributes][${stockLocationId}][count_on_hand]_input']`)
205
+ .forEach((el) => {
206
+ if (stockLocationId === newStockLocationId) {
207
+ el.classList.remove('d-none')
208
+ el.classList.add('d-block')
209
+ } else {
210
+ el.classList.remove('d-block')
211
+ el.classList.add('d-none')
212
+ }
213
+ })
214
+ })
215
+ this.updateShopLocationCountOnHand()
216
+ }
217
+
218
+ updateCurrency({ target: { value: newCurrency } }) {
219
+ this.currentCurrencyValue = newCurrency
220
+ this.currenciesValue.forEach((currency) => {
221
+ this.variantsContainerTarget
222
+ .querySelectorAll(
223
+ `.price-input-container:has(input[data-slot="[prices_attributes][${currency}][amount]_input"])`
224
+ )
225
+ .forEach((el) => {
226
+ if (currency === newCurrency) {
227
+ el.classList.remove('d-none')
228
+ el.classList.add('d-flex')
229
+ } else {
230
+ el.classList.remove('d-flex')
231
+ el.classList.add('d-none')
232
+ }
233
+ })
234
+ })
235
+ }
236
+
237
+ optionsValueChanged(value, previousValue) {
238
+ if (this.hasNewOptionButtonTarget) {
239
+ const label = this.newOptionButtonLabelTarget
240
+
241
+ if (Object.values(value).filter(Boolean).length) {
242
+ label.textContent = label.dataset.hasOptionsText
243
+ } else {
244
+ label.textContent = label.dataset.noOptionsText
245
+ }
246
+ }
247
+ this.refreshOptionNameSelect()
248
+ this.variantsValue = this.generateVariants(value)
249
+
250
+ // We want to clear the ignoredVariants when the options change
251
+ if (previousValue && Object.keys(previousValue).length === 0) return
252
+ this.ignoredVariants = new Set()
253
+ }
254
+
255
+ calculateVariantName(variant, keys, i) {
256
+ let name = ''
257
+ let internalName = name
258
+ if (i === 0) {
259
+ name = variant[keys[i]]
260
+ internalName = name
261
+ } else {
262
+ const namesPath = keys.slice(1, keys.length).map((key) => variant[key])
263
+ name = namesPath.join(' / ')
264
+ internalName = `${variant[keys[0]]}/${namesPath.join('/')}`
265
+ }
266
+
267
+ return { name, internalName }
268
+ }
269
+
270
+ updateParentCountOnHand(event) {
271
+ const { variantName } = event.target.closest('[data-variants-form-target="variant"]').dataset
272
+ const stockLocationId = event.target.dataset.stockLocationId
273
+ const childrenCountOnHand = this.variantsContainerTarget.querySelectorAll(
274
+ `[data-variant-name^="${variantName}/"] input[data-slot="[stock_items_attributes][${stockLocationId}][count_on_hand]_input"]`
275
+ )
276
+ const parentCountOnHand = event.target.value
277
+
278
+ childrenCountOnHand.forEach((countOnHandInput) => {
279
+ countOnHandInput.value = parentCountOnHand
280
+ })
281
+
282
+ const childrenCountOnHandKeys = this.variantsValue
283
+ .filter((variant) => variant.internalName.startsWith(variantName))
284
+ .map((variant) => variant.internalName)
285
+
286
+ childrenCountOnHandKeys.forEach((key) => {
287
+ this.updateStockItemForVariant(key, { count_on_hand: parentCountOnHand }, stockLocationId)
288
+ })
289
+
290
+ this.updateShopLocationCountOnHand()
291
+ }
292
+
293
+ updateParentPrice(event) {
294
+ const { variantName } = event.target.closest('[data-variants-form-target="variant"]').dataset
295
+ const currency = event.target.dataset.currency
296
+ const childrenPrices = this.variantsContainerTarget.querySelectorAll(
297
+ `[data-variant-name^="${variantName}/"] input[data-slot="[prices_attributes][${currency}][amount]_input"]`
298
+ )
299
+ const parentPrice = event.target.value
300
+
301
+ childrenPrices.forEach((priceInput) => {
302
+ priceInput.value = parentPrice
303
+ })
304
+ const childrenPricesKeys = this.variantsValue
305
+ .filter((variant) => variant.internalName.startsWith(variantName))
306
+ .map((variant) => variant.internalName)
307
+
308
+ childrenPricesKeys.forEach((key) => {
309
+ this.updatePriceForVariant(key, parentPrice, currency)
310
+ })
311
+ }
312
+
313
+ selectChildVariants(event) {
314
+ const { variantName } = event.target.closest('[data-variants-form-target="variant"]').dataset
315
+
316
+ const children = this.variantsContainerTarget.querySelectorAll(
317
+ `[data-variant-name^="${variantName}/"] input[type="checkbox"]`
318
+ )
319
+
320
+ children.forEach((child) => {
321
+ child.checked = event.target.checked
322
+ })
323
+
324
+ this.refresh()
325
+ }
326
+
327
+ prepareParentVariant(existingVariant, internalName) {
328
+ this.currenciesValue.forEach((currency) => {
329
+ const parentPriceInput = existingVariant.querySelector(
330
+ `input[data-slot="[prices_attributes][${currency}][amount]_input"]`
331
+ )
332
+ parentPriceInput.name = ''
333
+ parentPriceInput.dataset.action = `input->variants-form#updateParentPrice`
334
+ })
335
+ this.stockLocationsValue.forEach((stockLocationId) => {
336
+ const parentCountOnHandInput = existingVariant.querySelector(
337
+ `input[data-slot="[stock_items_attributes][${stockLocationId}][count_on_hand]_input"]`
338
+ )
339
+ parentCountOnHandInput.name = ''
340
+ parentCountOnHandInput.dataset.action = 'input->variants-form#updateParentCountOnHand'
341
+ })
342
+
343
+ const checkbox = existingVariant.querySelector('input[type="checkbox"][id^="checkbox_"]')
344
+ if (!checkbox) return
345
+ const checkboxLabel = existingVariant.querySelector('label[for^="checkbox_"]')
346
+ checkbox.id = `parent_checkbox_${internalName}`
347
+ checkboxLabel.htmlFor = `parent_checkbox_${internalName}`
348
+ checkbox.dataset.action = 'click->variants-form#selectChildVariants'
349
+ checkbox.value = internalName
350
+ }
351
+
352
+ updatePrice(event) {
353
+ const { variantName } = event.target.closest('[data-variants-form-target="variant"]').dataset
354
+ const currency = event.target.dataset.currency
355
+ const nestingLevel = variantName.split('/').length
356
+ this.updatePriceForVariant(variantName, event.target.value, currency)
357
+ if (nestingLevel > 1) {
358
+ const parentName = variantName.split('/')[0]
359
+ this.updateParentPriceRange(parentName, currency)
360
+ }
361
+ }
362
+
363
+ replaceBlankWithZero(event) {
364
+ if (event.target.value === '') {
365
+ event.target.value = 0
366
+ }
367
+ }
368
+
369
+ updateCountOnHand(event) {
370
+ const variantEl = event.target.closest('[data-variants-form-target="variant"]')
371
+ const variantName = variantEl.dataset.variantName
372
+
373
+ const nestingLevel = variantName.split('/').length
374
+ const stockLocationId = event.target.dataset.stockLocationId
375
+ const countOnHand = event.target.value || 0
376
+
377
+ this.updateStockItemForVariant(variantName, { count_on_hand: countOnHand }, stockLocationId)
378
+
379
+ if (nestingLevel > 1) {
380
+ const parentName = variantName.split('/')[0]
381
+ this.updateParentStockSum(parentName, stockLocationId)
382
+ }
383
+
384
+ this.updateShopLocationCountOnHand()
385
+ }
386
+
387
+ updateParentPriceRange(variantName, currency) {
388
+ const parentPriceEl = this.variantsContainerTarget.querySelector(
389
+ `div:not(.nested)[data-variant-name="${variantName}"] [data-slot="[prices_attributes][${currency}][amount]_input"]`
390
+ )
391
+ if (!parentPriceEl) return
392
+
393
+ const currentVariantKeys = Array.from(
394
+ this.variantsContainerTarget.querySelectorAll(`[data-variant-name^="${variantName}/"]`)
395
+ ).map((el) => el.dataset.variantName)
396
+
397
+ const pricesVariation = new Set(
398
+ Object.keys(this.pricesValue)
399
+ .filter((key) => currentVariantKeys.includes(key))
400
+ .map((key) => this.priceForVariant(key, currency).amount)
401
+ )
402
+
403
+ pricesVariation.delete(null)
404
+
405
+ if (pricesVariation.size === 0) {
406
+ parentPriceEl.value = this.priceForVariant(variantName, currency).amount
407
+ parentPriceEl.placeholder = ''
408
+ return
409
+ }
410
+ const minPrice = Math.min(...pricesVariation)
411
+ const maxPrice = Math.max(...pricesVariation)
412
+
413
+ if (minPrice !== maxPrice) {
414
+ parentPriceEl.value = null
415
+ parentPriceEl.placeholder = `${minPrice} - ${maxPrice}`
416
+ } else {
417
+ parentPriceEl.value = minPrice
418
+ }
419
+ }
420
+
421
+ updateParentStockSum(variantName, stockLocationId) {
422
+ const parentStockEl = this.variantsContainerTarget.querySelector(
423
+ `div:not(.nested)[data-variant-name="${variantName}"] [data-slot="[stock_items_attributes][${stockLocationId}][count_on_hand]_input"]`
424
+ )
425
+ if (!parentStockEl) return
426
+
427
+ const currentVariantKeys = Array.from(
428
+ this.variantsContainerTarget.querySelectorAll(`[data-variant-name^="${variantName}/"]`)
429
+ ).map((el) => el.dataset.variantName)
430
+
431
+ if (currentVariantKeys.length === 0) {
432
+ parentStockEl.value = this.stockItemForVariant(variantName, stockLocationId).count_on_hand
433
+ parentStockEl.placeholder = ''
434
+ this.updateShopLocationCountOnHand()
435
+ return
436
+ }
437
+
438
+ const countsOnHand = Object.keys(this.stockValue)
439
+ .filter((key) => currentVariantKeys.includes(key))
440
+ .map((key) => this.stockItemForVariant(key, stockLocationId).count_on_hand)
441
+
442
+ const sum = countsOnHand.reduce((acc, value) => acc + parseInt(value) || 0, 0)
443
+
444
+ parentStockEl.placeholder = String(sum)
445
+ parentStockEl.value = null
446
+ }
447
+
448
+ variantsValueChanged() {
449
+ let keys = Object.keys(this.variantsValue[0] || {}).filter((key) => key !== 'internalName')
450
+
451
+ const currentVariants = new Set()
452
+
453
+ if (keys.length) {
454
+ this.variantsTableTarget.classList.remove('d-none')
455
+
456
+ const nestingLevel = Math.min(keys.length, 2)
457
+ let idx = 0
458
+
459
+ for (let i = 0; i < nestingLevel; i++) {
460
+ this.variantsValue.forEach((variant) => {
461
+ const { name, internalName } = this.calculateVariantName(variant, keys, i)
462
+ if (currentVariants.has(internalName) || this.ignoredVariants.has(internalName)) {
463
+ idx++
464
+ return
465
+ }
466
+ currentVariants.add(internalName)
467
+
468
+ const existingVariant = this.variantsContainerTarget.querySelector(`[data-variant-name="${internalName}"]`)
469
+ if (existingVariant) {
470
+ if (i === 0 && nestingLevel > 1) {
471
+ existingVariant.querySelectorAll("input[type='hidden']").forEach((input) => input.remove())
472
+
473
+ this.prepareParentVariant(existingVariant, internalName)
474
+ }
475
+ idx++
476
+ return
477
+ }
478
+
479
+ const template = this.variantTemplateTarget.content.cloneNode(true)
480
+ const variantNameContainer = template.querySelector('[data-slot="variantName"]')
481
+ const variantTarget = template.querySelector('[data-variants-form-target="variant"]')
482
+ variantTarget.dataset.variantName = internalName
483
+
484
+ const variantId = this.variantIdsValue[internalName]
485
+ if (variantId) {
486
+ const variantEditButton = variantTarget.querySelector('[data-slot="variantEditButton"]')
487
+
488
+ if (variantEditButton) {
489
+ variantEditButton.href = `/admin/products/${this.productIdValue}/variants/${variantId}/edit`
490
+ variantEditButton.classList.remove('invisible')
491
+ }
492
+ }
493
+
494
+ let previousVariant = null
495
+
496
+ if (i > 0) {
497
+ const { internalName: parentInternalName } = this.calculateVariantName(variant, keys, 0)
498
+ const variantsInThisGroup = this.variantsContainerTarget.querySelectorAll(
499
+ `[data-variant-name^="${parentInternalName}/"]`
500
+ )
501
+ if (variantsInThisGroup.length > 0) {
502
+ // If there are already variants in this option type then we want to render this variant after the last variant in the group
503
+ previousVariant = variantsInThisGroup[variantsInThisGroup.length - 1]
504
+ } else {
505
+ // Otherwise we want to render this variant after the parent variant
506
+ previousVariant = this.variantsContainerTarget.querySelector(
507
+ `[data-variant-name="${parentInternalName}"]`
508
+ )
509
+ }
510
+ variantTarget.classList.add('nested')
511
+ } else if (nestingLevel > 1) {
512
+ template.querySelectorAll("input[type='hidden']").forEach((input) => input.remove())
513
+ this.prepareParentVariant(template, internalName)
514
+ }
515
+
516
+ if (i === nestingLevel - 1) {
517
+ const inputs = this.createInputsForVariant(keys, variant, idx)
518
+ inputs.forEach((input) => {
519
+ variantTarget.appendChild(input)
520
+ })
521
+
522
+ const checkbox = template.querySelector('input[type="checkbox"]#checkbox_')
523
+ if (checkbox) {
524
+ const checkboxLabel = template.querySelector('label[for="checkbox_"]')
525
+ checkbox.id = `checkbox_${internalName}`
526
+ checkboxLabel.htmlFor = `checkbox_${internalName}`
527
+ checkbox.value = internalName
528
+ }
529
+
530
+ this.preparePriceInputs(variantTarget, internalName, idx)
531
+
532
+ this.prepareStockInputs(variantTarget, internalName, idx)
533
+ }
534
+ idx++
535
+
536
+ variantNameContainer.textContent = name
537
+ if (previousVariant) {
538
+ previousVariant.after(template)
539
+ } else {
540
+ this.variantsContainerTarget.appendChild(template)
541
+ }
542
+ })
543
+ }
544
+ } else {
545
+ this.variantsTableTarget.classList.add('d-none')
546
+ }
547
+
548
+ this.variantsContainerTarget.querySelectorAll('[data-variants-form-target="variant"]').forEach((variant) => {
549
+ const variantName = variant.dataset.variantName
550
+ if (!currentVariants.has(variantName)) {
551
+ variant.remove()
552
+ }
553
+ })
554
+
555
+ // When going back from variant edit page the `variantsValueChanged` method is called before the `connect` method, so `this.productFormController` is not set yet
556
+ if (this.productFormController) {
557
+ this.productFormController.hasVariantsValue = this.variantsValue.length > 0
558
+ }
559
+
560
+ this.refreshParentInputs()
561
+ }
562
+
563
+ refreshParentInputs() {
564
+ const firstOption = Object.values(this.optionsValue)[0]
565
+ if (firstOption) {
566
+ firstOption.values.forEach((name) => {
567
+ this.currenciesValue.forEach((currency) => {
568
+ this.updateParentPriceRange(name, currency)
569
+ })
570
+ this.stockLocationsValue.forEach((stockLocationId) => {
571
+ this.updateParentStockSum(name, stockLocationId)
572
+ })
573
+ this.updateShopLocationCountOnHand()
574
+ })
575
+ }
576
+ }
577
+
578
+ createInputsForVariant(keys, variant, i) {
579
+ const inputs = []
580
+ if (this.variantIdsValue[variant.internalName]) {
581
+ const idInput = document.createElement('input')
582
+ idInput.type = 'hidden'
583
+ idInput.name = `product[variants_attributes][${i}][id]`
584
+ idInput.value = this.variantIdsValue[variant.internalName]
585
+ inputs.push(idInput)
586
+ }
587
+
588
+ keys.forEach((key) => {
589
+ const nameInput = document.createElement('input')
590
+ nameInput.type = 'hidden'
591
+ nameInput.name = `product[variants_attributes][${i}][options][][name]`
592
+ nameInput.value = key
593
+ inputs.push(nameInput)
594
+
595
+ const positionInput = document.createElement('input')
596
+ positionInput.type = 'hidden'
597
+ positionInput.name = `product[variants_attributes][${i}][options][][position]`
598
+ positionInput.value = Object.values(this.optionsValue)
599
+ .filter(Boolean)
600
+ .find((option) => option.name === key).position
601
+ inputs.push(positionInput)
602
+
603
+ const valueInput = document.createElement('input')
604
+ valueInput.type = 'hidden'
605
+ valueInput.name = `product[variants_attributes][${i}][options][][value]`
606
+ valueInput.value = variant[key]
607
+ inputs.push(valueInput)
608
+ })
609
+
610
+ return inputs
611
+ }
612
+
613
+ prepareStockInputs(variantTarget, internalName, idx) {
614
+ this.stockLocationsValue.forEach((stockLocationId) => {
615
+ const stockInput = variantTarget.querySelector(
616
+ `input[data-slot="[stock_items_attributes][${stockLocationId}][count_on_hand]_input"]`
617
+ )
618
+ const stockIdInput = variantTarget.querySelector(
619
+ `input[data-slot="[stock_items_attributes][${stockLocationId}][id]_input"]`
620
+ )
621
+ const stockLocationIdInput = variantTarget.querySelector(
622
+ `input[data-slot="[stock_items_attributes][${stockLocationId}][stock_location_id]_input"]`
623
+ )
624
+ let stockItem = this.stockItemForVariant(internalName, stockLocationId)
625
+ if (!stockItem.id) {
626
+ const oldInternalName = internalName.split('/').slice(0, -1).join('/')
627
+ const oldStock = this.stockItemForVariant(oldInternalName, stockLocationId).count_on_hand ?? 0
628
+
629
+ stockItem.count_on_hand = oldStock
630
+
631
+ this.updateStockItemForVariant(internalName, stockItem, stockLocationId)
632
+ }
633
+
634
+ stockInput.name = `product[variants_attributes][${idx}][stock_items_attributes][${stockLocationId}][count_on_hand]`
635
+ stockLocationIdInput.name = `product[variants_attributes][${idx}][stock_items_attributes][${stockLocationId}][stock_location_id]`
636
+ stockIdInput.name = `product[variants_attributes][${idx}][stock_items_attributes][${stockLocationId}][id]`
637
+
638
+ stockInput.value = stockItem.count_on_hand
639
+ if (String(stockLocationId) === String(this.currentStockLocationIdValue)) {
640
+ stockInput.classList.remove('d-none')
641
+ stockInput.classList.add('d-block')
642
+ } else {
643
+ stockInput.classList.remove('d-block')
644
+ stockInput.classList.add('d-none')
645
+ }
646
+ if (stockItem.id) {
647
+ stockIdInput.value = stockItem.id
648
+ }
649
+ })
650
+ }
651
+
652
+ preparePriceInputs(variantTarget, internalName, idx) {
653
+ this.currenciesValue.forEach((currency, currencyIndex) => {
654
+ const priceInput = variantTarget.querySelector(
655
+ `input[data-slot="[prices_attributes][${currency}][amount]_input"]`
656
+ )
657
+ const currencyInput = variantTarget.querySelector(
658
+ `input[data-slot="[prices_attributes][${currency}][currency]_input"]`
659
+ )
660
+ const idInput = variantTarget.querySelector(`input[data-slot="[prices_attributes][${currency}][id]_input"]`)
661
+ priceInput.name = `product[variants_attributes][${idx}][prices_attributes][${currency}]`
662
+ if (currency === this.currentCurrencyValue) {
663
+ priceInput.parentElement.classList.remove('d-none')
664
+ priceInput.parentElement.classList.add('d-flex')
665
+ } else {
666
+ priceInput.parentElement.classList.remove('d-flex')
667
+ priceInput.parentElement.classList.add('d-none')
668
+ }
669
+
670
+ const existingPrice = this.priceForVariant(internalName, currency)
671
+ priceInput.value = existingPrice.amount
672
+ currencyInput.value = currency
673
+ if (existingPrice.id) {
674
+ idInput.value = existingPrice.id
675
+ }
676
+
677
+ priceInput.name = `product[variants_attributes][${idx}][prices_attributes][${currencyIndex}][amount]`
678
+ currencyInput.name = `product[variants_attributes][${idx}][prices_attributes][${currencyIndex}][currency]`
679
+ idInput.name = `product[variants_attributes][${idx}][prices_attributes][${currencyIndex}][id]`
680
+ })
681
+ }
682
+
683
+ addOption(name, option_values = [], id) {
684
+ let color = false
685
+ let position = Object.keys(this.optionsValue).length + 1
686
+ let newOptionsPositions = {}
687
+
688
+ const template = this.optionTemplate(name, option_values, id, color)
689
+
690
+ this.optionsValue = {
691
+ ...this.optionsValue,
692
+ ...newOptionsPositions,
693
+ [id]: {
694
+ name,
695
+ values: option_values,
696
+ position: position
697
+ }
698
+ }
699
+
700
+ if (position === 1) {
701
+ this.optionsContainerTarget.prepend(document.importNode(template, true))
702
+ } else {
703
+ this.optionsContainerTarget.appendChild(document.importNode(template, true))
704
+ }
705
+ }
706
+
707
+ // After selecting an option name (eg. "Color"), we fetch the option values for that option name and update the tom select options
708
+ async handleSelectedOptionName(event) {
709
+ const targetInput = event.target
710
+ this.lastOptionNameId = targetInput.value
711
+
712
+ if (this.lastOptionNameId) {
713
+ const response = await get(`/admin/option_types/${this.lastOptionNameId}/option_values/select_options`)
714
+
715
+ if (response.ok) {
716
+ this.currentOptionValues[this.lastOptionNameId] = await response.json
717
+
718
+ const optionsCreatorContainer = targetInput.closest('.options-creator__option')
719
+ const newOptionValuesSelects = optionsCreatorContainer.querySelectorAll('[data-variants-form-target="newOptionValuesSelect"]')
720
+
721
+ newOptionValuesSelects.forEach((select) => this.replaceSelectOptions(select))
722
+ }
723
+ }
724
+ }
725
+
726
+ replaceSelectOptions(select) {
727
+ const tomSelect = select.tomselect
728
+
729
+ if (tomSelect) {
730
+ tomSelect.clear()
731
+ tomSelect.clearOptions()
732
+ tomSelect.addOptions(this.currentOptionValues[this.lastOptionNameId])
733
+ }
734
+ }
735
+
736
+ newOptionValuesSelectTargetConnected(select) {
737
+ if (this.lastOptionNameId)
738
+ this.replaceSelectOptions(select)
739
+ }
740
+
741
+ handleNewOption(_event) {
742
+ const newOptionName = this.newOptionNameInputTarget.options[this.newOptionNameInputTarget.selectedIndex].text
743
+ const newOptionId = String(this.newOptionNameInputTarget.value)
744
+ const newOptionValues = this.newOptionValuesSelectContainerTarget.values()
745
+
746
+ if (
747
+ !newOptionName.length ||
748
+ this.optionsValue[newOptionId] ||
749
+ newOptionValues.length === 0 ||
750
+ Object.values(this.optionsValue)
751
+ .filter(Boolean)
752
+ .map((v) => v.name)
753
+ .includes(newOptionName)
754
+ ) {
755
+ return
756
+ }
757
+
758
+ this.addOption(newOptionName, newOptionValues, newOptionId)
759
+
760
+ this.hideNewOptionForm()
761
+ }
762
+
763
+ hideNewOptionForm() {
764
+ this.newOptionNameInputTarget.tomselect.clear()
765
+ this.newOptionValuesSelectContainerTarget.reset()
766
+
767
+ this.newOptionFormTarget.classList.add('d-none')
768
+
769
+ this.newOptionButtonTarget.classList.remove('d-none')
770
+ }
771
+
772
+ editOption(event) {
773
+ let { optionId } = event.params
774
+ optionId = String(optionId)
775
+
776
+ const option = this.optionsContainerTarget.querySelector(`#option-${optionId}`)
777
+
778
+ const { name, values } = this.optionsValue[optionId]
779
+ const availableOptions = this.availableOptionsValue[optionId] || this.currentOptionValues[optionId]?.map((option) => ({ id: option.id, name: option.name }))
780
+
781
+ const form = this.optionFormTemplate(name, values, optionId, availableOptions)
782
+
783
+ option.replaceWith(form)
784
+
785
+ // Disable the option name in the select tag that is already picked
786
+ const optionContainer = this.optionsContainerTarget.querySelector(`#option-${optionId}`)
787
+ const optionNameSelect = optionContainer.querySelector('select[name="option_name"]')
788
+ const options = Array.from(optionNameSelect.options)
789
+
790
+ const alreadySelectedOptions = Object.keys(this.optionsValue).filter((k) => this.optionsValue[k] !== null)
791
+ const optionsToDisable = options.filter((option) => option.text != name && alreadySelectedOptions.includes(option.value))
792
+
793
+ optionsToDisable.forEach((option) => option.disabled = true)
794
+ }
795
+
796
+ saveOption(event) {
797
+ let { optionId } = event.params
798
+ optionId = String(optionId)
799
+
800
+ const option = this.optionsContainerTarget.querySelector(`#option-${optionId}`)
801
+ const optionForm = option.closest('[data-slot="optionForm"]')
802
+ const optionNameSelect = option.querySelector('select[name="option_name"]')
803
+ const optionName = optionNameSelect.options[optionNameSelect.selectedIndex].text
804
+ const newId = String(optionNameSelect.value)
805
+
806
+ if ((newId != optionId && this.optionsValue[newId]) || !optionName.length) {
807
+ return
808
+ }
809
+
810
+ let color = false
811
+ let position = this.optionsValue[optionId].position
812
+ let newOptionsPositions = {}
813
+
814
+ const optionValues = option.querySelector('[data-slot="optionValuesSelectContainer"]').values()
815
+
816
+ if (optionValues.length === 0) {
817
+ return
818
+ }
819
+
820
+ const template = this.optionTemplate(optionName, optionValues, newId, color)
821
+
822
+ if (color) {
823
+ this.optionsContainerTarget.prepend(template)
824
+ optionForm.remove()
825
+ } else {
826
+ optionForm.replaceWith(template)
827
+ }
828
+
829
+ const newOptions = {
830
+ ...this.optionsValue,
831
+ ...newOptionsPositions,
832
+ [newId]: {
833
+ ...this.optionsValue[optionId],
834
+ name: optionName,
835
+ values: optionValues,
836
+ position: position
837
+ }
838
+ }
839
+ if (optionId != newId) {
840
+ delete newOptions[optionId]
841
+ }
842
+
843
+ this.optionsValue = newOptions
844
+ }
845
+
846
+ showNewOptionForm(event) {
847
+ event.preventDefault()
848
+ this.newOptionFormTarget.classList.remove('d-none')
849
+ this.newOptionButtonTarget.classList.add('d-none')
850
+ this.refreshOptionNameSelect()
851
+ }
852
+
853
+ optionTemplate(name, values, id, color = false) {
854
+ const template = this.optionTemplateTarget.content.cloneNode(true)
855
+ template.querySelectorAll('[data-variants-form-option-id-param]').forEach((el) => {
856
+ el.dataset.variantsFormOptionIdParam = id
857
+ })
858
+ const optionName = template.querySelector('[data-slot="optionName"]')
859
+ const optionValuesTemplates = this.optionValueTemplate(values)
860
+ const optionValuesContainer = template.querySelector('[data-slot="optionValuesContainer"]')
861
+ optionValuesTemplates.forEach((optionValueTemplate) => {
862
+ optionValuesContainer.appendChild(optionValueTemplate)
863
+ })
864
+ const mainContainer = template.querySelector('[data-variants-form-target="option"]')
865
+ mainContainer.id = `option-${id}`
866
+
867
+ optionName.textContent = name
868
+ if (color) {
869
+ mainContainer.classList.add('color-option')
870
+ mainContainer.querySelector('.draggable').disabled = true
871
+ }
872
+ return template
873
+ }
874
+
875
+ discardOption(event) {
876
+ let { optionId } = event.params
877
+ optionId = String(optionId)
878
+
879
+ const option = this.optionsContainerTarget.querySelector(`#option-${optionId}`)
880
+ option.remove()
881
+ this.optionsValue = { ...this.optionsValue, [optionId]: null }
882
+ this.refreshParentInputs()
883
+ }
884
+
885
+ optionFormTemplate(optionName, optionValues, id, availableOptions) {
886
+ const template = this.optionFormTemplateTarget.content.cloneNode(true)
887
+
888
+ const optionNameSelect = template.querySelector('select[name="option_name"]')
889
+
890
+ let optionExists = false
891
+ // If options includes the optionName, set it as selected
892
+ optionNameSelect.querySelectorAll('option').forEach((option) => {
893
+ if (String(option.value) === id) {
894
+ option.selected = true
895
+ optionExists = true
896
+ }
897
+ })
898
+ // Otherwise, create a new option, and select it
899
+ if (!optionExists) {
900
+ const newOption = document.createElement('option')
901
+ newOption.text = optionName
902
+ newOption.value = id
903
+ newOption.selected = true
904
+ optionNameSelect.appendChild(newOption)
905
+ }
906
+
907
+ const optionValuesSelectContainer = template.querySelector('[data-slot="optionValuesSelectContainer"]')
908
+ const tomSelectOptionValues = optionValues.map((optionValue) => {
909
+ return {
910
+ id: availableOptions.find((availableOption) => availableOption.name === optionValue)?.id,
911
+ name: optionValue
912
+ }
913
+ })
914
+
915
+ optionValuesSelectContainer.setAttribute('data-multi-tom-select-preloaded-options-value', JSON.stringify(availableOptions))
916
+ optionValuesSelectContainer.setAttribute('data-multi-tom-select-preloaded-values-value', JSON.stringify(tomSelectOptionValues))
917
+
918
+ template.querySelectorAll('[data-variants-form-option-id-param]').forEach((el) => {
919
+ el.dataset.variantsFormOptionIdParam = id
920
+ })
921
+
922
+ const mainContainer = template.querySelector('[data-slot="optionForm"]')
923
+ mainContainer.id = `option-${id}`
924
+
925
+ return template
926
+ }
927
+
928
+ optionValueTemplate(values) {
929
+ const templates = []
930
+
931
+ values.forEach((value) => {
932
+ const template = this.optionValueTemplateTarget.content.cloneNode(true)
933
+ const optionValueNameEl = template.querySelector('[data-slot="optionValueName"]')
934
+ optionValueNameEl.textContent = value
935
+ optionValueNameEl.dataset.name = value
936
+
937
+ templates.push(template)
938
+ })
939
+
940
+ return templates
941
+ }
942
+
943
+ refreshOptionNameSelect() {
944
+ const alreadySelectedOptions = Object.keys(this.optionsValue).filter((k) => this.optionsValue[k] !== null)
945
+
946
+ Array.from(this.newOptionNameInputTarget.options).forEach((option) => {
947
+ const tomSelect = this.newOptionNameInputTarget.tomselect
948
+ if (!tomSelect) return
949
+
950
+ const tomselectOption = tomSelect.getOption(option.value)
951
+ if (!tomselectOption) return
952
+
953
+ const alreadySelected = alreadySelectedOptions.includes(option.value)
954
+ tomselectOption.ariaDisabled = alreadySelected
955
+
956
+ if (alreadySelected) {
957
+ tomselectOption.removeAttribute('data-selectable')
958
+ } else {
959
+ tomselectOption.setAttribute('data-selectable', '')
960
+ }
961
+
962
+ tomselectOption.disabled = alreadySelected
963
+ })
964
+
965
+ const optionNameTomSelect = this.newOptionNameInputTarget.tomselect
966
+
967
+ if (optionNameTomSelect) {
968
+ optionNameTomSelect.sync()
969
+ optionNameTomSelect.refreshOptions(false)
970
+ }
971
+ }
972
+
973
+ generateVariants(optionsValue) {
974
+ const options = Object.values(optionsValue)
975
+ .filter(Boolean)
976
+ .sort((a, b) => a.position - b.position)
977
+
978
+ if (options.length === 0) {
979
+ return []
980
+ }
981
+
982
+ const optionValues = options.map((option) => option.values)
983
+ const optionNames = options.map((option) => option.name)
984
+
985
+ const cartesianProduct = this.cartesianProduct(optionValues)
986
+
987
+ return cartesianProduct.map((variant) => {
988
+ variant = Array.isArray(variant) ? variant : [variant]
989
+ const variantObj = variant.reduce((acc, value, index) => {
990
+ acc[options[index].name] = value
991
+ return acc
992
+ }, {})
993
+ variantObj.internalName = this.calculateVariantName(variantObj, optionNames, optionNames.length - 1).internalName
994
+ return variantObj
995
+ })
996
+ }
997
+
998
+ cartesianProduct(arr) {
999
+ return arr.reduce((a, b) => a.flatMap((d) => b.map((e) => [d, e].flat())))
1000
+ }
1001
+
1002
+ stockItemForVariant(variantName, stockLocationId) {
1003
+ const existingStock = this.stockValue[variantName]?.[String(stockLocationId)]
1004
+ if (existingStock) return existingStock
1005
+
1006
+ return { count_on_hand: 0, backorderable: false, id: null }
1007
+ }
1008
+
1009
+ updateStockItemForVariant(variantName, newStockItem, stockLocationId) {
1010
+ const existingStockItem = this.stockItemForVariant(variantName, stockLocationId)
1011
+ this.stockValue = {
1012
+ ...this.stockValue,
1013
+ [variantName]: {
1014
+ ...this.stockValue[variantName],
1015
+ [stockLocationId]: {
1016
+ ...existingStockItem,
1017
+ ...newStockItem
1018
+ }
1019
+ }
1020
+ }
1021
+ }
1022
+
1023
+ priceForVariant(variantName, currency) {
1024
+ const existingPrice = this.pricesValue[variantName]?.[currency.toLowerCase()]
1025
+ if (existingPrice) {
1026
+ return {
1027
+ ...existingPrice,
1028
+ amount: existingPrice.amount ? parseFloat(existingPrice.amount) : existingPrice.amount
1029
+ }
1030
+ }
1031
+
1032
+ return { amount: null, id: null }
1033
+ }
1034
+
1035
+ updatePriceForVariant(variantName, newPrice, currency) {
1036
+ const existingPrice = this.priceForVariant(variantName, currency)
1037
+ this.pricesValue = {
1038
+ ...this.pricesValue,
1039
+ [variantName]: {
1040
+ ...this.pricesValue[variantName],
1041
+ [currency.toLowerCase()]: {
1042
+ ...existingPrice,
1043
+ amount: parseFloat(newPrice)
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+ }