spree_page_builder 5.3.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +43 -0
  3. data/Rakefile +18 -0
  4. data/app/controllers/concerns/spree/admin/page_builder_concern.rb +45 -0
  5. data/app/controllers/concerns/spree/admin/storefront_breadcrumb_concern.rb +12 -0
  6. data/app/controllers/spree/admin/page_blocks_controller.rb +58 -0
  7. data/app/controllers/spree/admin/page_links_controller.rb +60 -0
  8. data/app/controllers/spree/admin/page_sections_controller.rb +82 -0
  9. data/app/controllers/spree/admin/pages_controller.rb +39 -0
  10. data/app/controllers/spree/admin/storefront_controller.rb +41 -0
  11. data/app/controllers/spree/admin/themes_controller.rb +87 -0
  12. data/app/helpers/spree/admin/page_builder_helper.rb +125 -0
  13. data/app/helpers/spree/admin/themes_helper.rb +9 -0
  14. data/app/jobs/spree/page_builder/products/touch_taxons_job_decorator.rb +15 -0
  15. data/app/jobs/spree/themes/duplicate_components_job.rb +59 -0
  16. data/app/jobs/spree/themes/screenshot_job.rb +81 -0
  17. data/app/models/concerns/spree/has_page_links.rb +53 -0
  18. data/app/models/spree/page.rb +188 -0
  19. data/app/models/spree/page_block.rb +73 -0
  20. data/app/models/spree/page_blocks/buttons.rb +29 -0
  21. data/app/models/spree/page_blocks/heading.rb +18 -0
  22. data/app/models/spree/page_blocks/image.rb +20 -0
  23. data/app/models/spree/page_blocks/link.rb +21 -0
  24. data/app/models/spree/page_blocks/mega_nav.rb +33 -0
  25. data/app/models/spree/page_blocks/mega_nav_with_subcategories.rb +32 -0
  26. data/app/models/spree/page_blocks/metafields.rb +18 -0
  27. data/app/models/spree/page_blocks/nav.rb +15 -0
  28. data/app/models/spree/page_blocks/newsletter_form.rb +18 -0
  29. data/app/models/spree/page_blocks/products/brand.rb +15 -0
  30. data/app/models/spree/page_blocks/products/buy_buttons.rb +24 -0
  31. data/app/models/spree/page_blocks/products/description.rb +18 -0
  32. data/app/models/spree/page_blocks/products/price.rb +18 -0
  33. data/app/models/spree/page_blocks/products/quantity_selector.rb +20 -0
  34. data/app/models/spree/page_blocks/products/share.rb +8 -0
  35. data/app/models/spree/page_blocks/products/title.rb +19 -0
  36. data/app/models/spree/page_blocks/products/variant_picker.rb +13 -0
  37. data/app/models/spree/page_blocks/subheading.rb +17 -0
  38. data/app/models/spree/page_blocks/text.rb +16 -0
  39. data/app/models/spree/page_builder/policy_decorator.rb +17 -0
  40. data/app/models/spree/page_builder/post_decorator.rb +17 -0
  41. data/app/models/spree/page_builder/product_decorator.rb +17 -0
  42. data/app/models/spree/page_builder/store_decorator.rb +46 -0
  43. data/app/models/spree/page_builder/taxon_decorator.rb +45 -0
  44. data/app/models/spree/page_link.rb +60 -0
  45. data/app/models/spree/page_section.rb +222 -0
  46. data/app/models/spree/page_sections/announcement_bar.rb +28 -0
  47. data/app/models/spree/page_sections/breadcrumbs.rb +12 -0
  48. data/app/models/spree/page_sections/collection_banner.rb +18 -0
  49. data/app/models/spree/page_sections/custom_code.rb +11 -0
  50. data/app/models/spree/page_sections/featured_posts.rb +45 -0
  51. data/app/models/spree/page_sections/featured_product.rb +50 -0
  52. data/app/models/spree/page_sections/featured_taxon.rb +90 -0
  53. data/app/models/spree/page_sections/featured_taxons.rb +45 -0
  54. data/app/models/spree/page_sections/footer.rb +101 -0
  55. data/app/models/spree/page_sections/header.rb +62 -0
  56. data/app/models/spree/page_sections/image_banner.rb +55 -0
  57. data/app/models/spree/page_sections/image_with_text.rb +65 -0
  58. data/app/models/spree/page_sections/main_password_footer.rb +18 -0
  59. data/app/models/spree/page_sections/main_password_header.rb +20 -0
  60. data/app/models/spree/page_sections/newsletter.rb +54 -0
  61. data/app/models/spree/page_sections/page_title.rb +19 -0
  62. data/app/models/spree/page_sections/post_details.rb +19 -0
  63. data/app/models/spree/page_sections/post_grid.rb +19 -0
  64. data/app/models/spree/page_sections/product_details.rb +53 -0
  65. data/app/models/spree/page_sections/product_grid.rb +13 -0
  66. data/app/models/spree/page_sections/related_products.rb +58 -0
  67. data/app/models/spree/page_sections/rich_text.rb +31 -0
  68. data/app/models/spree/page_sections/taxon_banner.rb +18 -0
  69. data/app/models/spree/page_sections/taxon_grid.rb +17 -0
  70. data/app/models/spree/page_sections/video.rb +107 -0
  71. data/app/models/spree/pages/account.rb +19 -0
  72. data/app/models/spree/pages/cart.rb +19 -0
  73. data/app/models/spree/pages/checkout.rb +15 -0
  74. data/app/models/spree/pages/custom.rb +38 -0
  75. data/app/models/spree/pages/homepage.rb +72 -0
  76. data/app/models/spree/pages/login.rb +19 -0
  77. data/app/models/spree/pages/password.rb +59 -0
  78. data/app/models/spree/pages/post.rb +27 -0
  79. data/app/models/spree/pages/post_list.rb +36 -0
  80. data/app/models/spree/pages/product_details.rb +30 -0
  81. data/app/models/spree/pages/search_results.rb +43 -0
  82. data/app/models/spree/pages/shop_all.rb +40 -0
  83. data/app/models/spree/pages/taxon.rb +29 -0
  84. data/app/models/spree/pages/taxon_list.rb +41 -0
  85. data/app/models/spree/pages/wishlist.rb +15 -0
  86. data/app/models/spree/theme.rb +233 -0
  87. data/app/models/spree/themes/default.rb +97 -0
  88. data/app/services/spree/taxons/touch_featured_sections.rb +21 -0
  89. data/app/views/layouts/spree/page_builder.html.erb +46 -0
  90. data/app/views/spree/admin/dashboard/_store_preview.html.erb +38 -0
  91. data/app/views/spree/admin/page_blocks/_form_tab_buttons.html.erb +5 -0
  92. data/app/views/spree/admin/page_blocks/create.turbo_stream.erb +4 -0
  93. data/app/views/spree/admin/page_blocks/destroy.turbo_stream.erb +9 -0
  94. data/app/views/spree/admin/page_blocks/edit.html.erb +43 -0
  95. data/app/views/spree/admin/page_blocks/forms/_brand.html.erb +15 -0
  96. data/app/views/spree/admin/page_blocks/forms/_brand_logo.html.erb +4 -0
  97. data/app/views/spree/admin/page_blocks/forms/_buttons.html.erb +13 -0
  98. data/app/views/spree/admin/page_blocks/forms/_heading.html.erb +28 -0
  99. data/app/views/spree/admin/page_blocks/forms/_image.html.erb +10 -0
  100. data/app/views/spree/admin/page_blocks/forms/_link.html.erb +19 -0
  101. data/app/views/spree/admin/page_blocks/forms/_mega_nav.html.erb +14 -0
  102. data/app/views/spree/admin/page_blocks/forms/_mega_nav_with_subcategories.html.erb +9 -0
  103. data/app/views/spree/admin/page_blocks/forms/_metafields.html.erb +35 -0
  104. data/app/views/spree/admin/page_blocks/forms/_nav.html.erb +6 -0
  105. data/app/views/spree/admin/page_blocks/forms/_newsletter_form.html.erb +20 -0
  106. data/app/views/spree/admin/page_blocks/forms/_price.html.erb +15 -0
  107. data/app/views/spree/admin/page_blocks/forms/_share.html.erb +5 -0
  108. data/app/views/spree/admin/page_blocks/forms/_subheading.html.erb +26 -0
  109. data/app/views/spree/admin/page_blocks/forms/_text.html.erb +21 -0
  110. data/app/views/spree/admin/page_blocks/forms/_title.html.erb +20 -0
  111. data/app/views/spree/admin/page_blocks/forms/_variant_picker.html.erb +10 -0
  112. data/app/views/spree/admin/page_blocks/forms/products/_buy_buttons.html.erb +10 -0
  113. data/app/views/spree/admin/page_blocks/forms/products/_description.html.erb +0 -0
  114. data/app/views/spree/admin/page_blocks/forms/products/_quantity_selector.html.erb +10 -0
  115. data/app/views/spree/admin/page_blocks/move_higher.turbo_stream.erb +4 -0
  116. data/app/views/spree/admin/page_blocks/move_lower.turbo_stream.erb +4 -0
  117. data/app/views/spree/admin/page_blocks/show.html.erb +1 -0
  118. data/app/views/spree/admin/page_blocks/update.turbo_stream.erb +6 -0
  119. data/app/views/spree/admin/page_builder/_add_block.html.erb +22 -0
  120. data/app/views/spree/admin/page_builder/_color_palette.html.erb +17 -0
  121. data/app/views/spree/admin/page_builder/_color_picker.html.erb +26 -0
  122. data/app/views/spree/admin/page_builder/_header.html.erb +113 -0
  123. data/app/views/spree/admin/page_builder/_labeled_range_input.html.erb +10 -0
  124. data/app/views/spree/admin/page_builder/_pages_dropdown.html.erb +17 -0
  125. data/app/views/spree/admin/page_builder/_range_input.html.erb +12 -0
  126. data/app/views/spree/admin/page_builder/_sidebar.html.erb +12 -0
  127. data/app/views/spree/admin/page_builder/_sidebar_block.html.erb +30 -0
  128. data/app/views/spree/admin/page_builder/_sidebar_colors.html.erb +86 -0
  129. data/app/views/spree/admin/page_builder/_sidebar_fonts.html.erb +85 -0
  130. data/app/views/spree/admin/page_builder/_sidebar_section.html.erb +44 -0
  131. data/app/views/spree/admin/page_builder/_sidebar_sections.html.erb +38 -0
  132. data/app/views/spree/admin/page_builder/_sidebar_sections_toolbar.html.erb +180 -0
  133. data/app/views/spree/admin/page_links/_form.html.erb +10 -0
  134. data/app/views/spree/admin/page_links/_linkable_type_dropdown.html.erb +15 -0
  135. data/app/views/spree/admin/page_links/_list.html.erb +36 -0
  136. data/app/views/spree/admin/page_links/_refresh_theme_preview.turbo_stream.erb +5 -0
  137. data/app/views/spree/admin/page_links/create.turbo_stream.erb +10 -0
  138. data/app/views/spree/admin/page_links/destroy.turbo_stream.erb +15 -0
  139. data/app/views/spree/admin/page_links/edit.html.erb +27 -0
  140. data/app/views/spree/admin/page_links/update.turbo_stream.erb +14 -0
  141. data/app/views/spree/admin/page_sections/_form_tab_buttons.html.erb +8 -0
  142. data/app/views/spree/admin/page_sections/create.turbo_stream.erb +11 -0
  143. data/app/views/spree/admin/page_sections/destroy.turbo_stream.erb +16 -0
  144. data/app/views/spree/admin/page_sections/edit.html.erb +39 -0
  145. data/app/views/spree/admin/page_sections/fields/_background_color.html.erb +6 -0
  146. data/app/views/spree/admin/page_sections/fields/_border_bottom_width.html.erb +5 -0
  147. data/app/views/spree/admin/page_sections/fields/_border_color.html.erb +7 -0
  148. data/app/views/spree/admin/page_sections/fields/_border_top_width.html.erb +6 -0
  149. data/app/views/spree/admin/page_sections/fields/_bottom_padding.html.erb +5 -0
  150. data/app/views/spree/admin/page_sections/fields/_button.html.erb +15 -0
  151. data/app/views/spree/admin/page_sections/fields/_header_layout.html.erb +26 -0
  152. data/app/views/spree/admin/page_sections/fields/_height.html.erb +6 -0
  153. data/app/views/spree/admin/page_sections/fields/_text_color.html.erb +7 -0
  154. data/app/views/spree/admin/page_sections/fields/_top_padding.html.erb +6 -0
  155. data/app/views/spree/admin/page_sections/forms/_announcement_bar.html.erb +5 -0
  156. data/app/views/spree/admin/page_sections/forms/_brand_story.html.erb +0 -0
  157. data/app/views/spree/admin/page_sections/forms/_breadcrumbs.html.erb +0 -0
  158. data/app/views/spree/admin/page_sections/forms/_collection_banner.html.erb +0 -0
  159. data/app/views/spree/admin/page_sections/forms/_custom_code.html.erb +4 -0
  160. data/app/views/spree/admin/page_sections/forms/_featured_posts.html.erb +33 -0
  161. data/app/views/spree/admin/page_sections/forms/_featured_product.html.erb +4 -0
  162. data/app/views/spree/admin/page_sections/forms/_featured_taxon.html.erb +59 -0
  163. data/app/views/spree/admin/page_sections/forms/_featured_taxons.html.erb +13 -0
  164. data/app/views/spree/admin/page_sections/forms/_footer.html.erb +20 -0
  165. data/app/views/spree/admin/page_sections/forms/_header.html.erb +12 -0
  166. data/app/views/spree/admin/page_sections/forms/_image_banner.html.erb +13 -0
  167. data/app/views/spree/admin/page_sections/forms/_image_with_text.html.erb +28 -0
  168. data/app/views/spree/admin/page_sections/forms/_main_password_footer.html.erb +9 -0
  169. data/app/views/spree/admin/page_sections/forms/_main_password_header.erb +13 -0
  170. data/app/views/spree/admin/page_sections/forms/_newsletter.html.erb +15 -0
  171. data/app/views/spree/admin/page_sections/forms/_page_title.html.erb +4 -0
  172. data/app/views/spree/admin/page_sections/forms/_post_details.html.erb +0 -0
  173. data/app/views/spree/admin/page_sections/forms/_post_grid.html.erb +0 -0
  174. data/app/views/spree/admin/page_sections/forms/_product_details.html.erb +1 -0
  175. data/app/views/spree/admin/page_sections/forms/_product_grid.html.erb +1 -0
  176. data/app/views/spree/admin/page_sections/forms/_related_products.html.erb +32 -0
  177. data/app/views/spree/admin/page_sections/forms/_rich_text.html.erb +0 -0
  178. data/app/views/spree/admin/page_sections/forms/_taxon_banner.html.erb +0 -0
  179. data/app/views/spree/admin/page_sections/forms/_taxon_grid.html.erb +5 -0
  180. data/app/views/spree/admin/page_sections/forms/_video.html.erb +15 -0
  181. data/app/views/spree/admin/page_sections/move_higher.turbo_stream.erb +7 -0
  182. data/app/views/spree/admin/page_sections/move_lower.turbo_stream.erb +7 -0
  183. data/app/views/spree/admin/page_sections/new.html.erb +26 -0
  184. data/app/views/spree/admin/page_sections/remove_attachment.turbo_stream.erb +4 -0
  185. data/app/views/spree/admin/page_sections/restore_design_settings_to_defaults.turbo_stream.erb +4 -0
  186. data/app/views/spree/admin/page_sections/show.html.erb +1 -0
  187. data/app/views/spree/admin/page_sections/update.turbo_stream.erb +10 -0
  188. data/app/views/spree/admin/pages/_form.html.erb +20 -0
  189. data/app/views/spree/admin/pages/_table_header.html.erb +6 -0
  190. data/app/views/spree/admin/pages/_table_row.html.erb +28 -0
  191. data/app/views/spree/admin/pages/edit.html.erb +13 -0
  192. data/app/views/spree/admin/pages/filters.html.erb +7 -0
  193. data/app/views/spree/admin/pages/index.html.erb +11 -0
  194. data/app/views/spree/admin/pages/new.html.erb +1 -0
  195. data/app/views/spree/admin/storefront/edit.html.erb +89 -0
  196. data/app/views/spree/admin/themes/_theme.html.erb +56 -0
  197. data/app/views/spree/admin/themes/_theme_preview_image.html.erb +17 -0
  198. data/app/views/spree/admin/themes/edit.html.erb +14 -0
  199. data/app/views/spree/admin/themes/index.html.erb +80 -0
  200. data/app/views/spree/admin/themes/update.turbo_stream.erb +1 -0
  201. data/config/initializers/spree_admin_navigation.rb +46 -0
  202. data/config/initializers/spree_admin_partials.rb +3 -0
  203. data/config/locales/en.yml +42 -0
  204. data/config/routes.rb +43 -0
  205. data/db/migrate/20250120094216_create_page_builder_models.rb +78 -0
  206. data/db/migrate/20250305121352_remove_page_builder_indices.rb +11 -0
  207. data/db/migrate/20250825175217_add_missing_page_builder_indexes.rb +7 -0
  208. data/db/migrate/20250913130044_add_page_links_counter_cache_to_spree_stores.rb +10 -0
  209. data/lib/generators/spree/page_builder/install/install_generator.rb +23 -0
  210. data/lib/spree/page_builder/engine.rb +185 -0
  211. data/lib/spree/page_builder/testing_support/factories/page_block_factory.rb +22 -0
  212. data/lib/spree/page_builder/testing_support/factories/page_factory.rb +33 -0
  213. data/lib/spree/page_builder/testing_support/factories/page_link_factory.rb +7 -0
  214. data/lib/spree/page_builder/testing_support/factories/page_section_factory.rb +27 -0
  215. data/lib/spree/page_builder/testing_support/factories/theme_factory.rb +14 -0
  216. data/lib/spree/page_builder/testing_support/factories.rb +3 -0
  217. data/lib/spree/page_builder.rb +4 -0
  218. data/lib/spree_page_builder.rb +1 -0
  219. metadata +288 -0
@@ -0,0 +1,28 @@
1
+ <tr id="<%= dom_id page %>" data-controller="row-link" class="cursor-pointer">
2
+ <td scope="row" data-action="click->row-link#openLink" class="w-60">
3
+ <%= link_to page.name, spree.edit_admin_theme_path(current_store.default_theme, page_id: page.id), data: { row_link_target: :link, turbo_frame: '_top', turbo_prefetch: false } %>
4
+ </td>
5
+ <td data-action="click->row-link#openLink" class="w-15">
6
+ <%= spree_time_ago(page.created_at) %>
7
+ </td>
8
+ <td data-action="click->row-link#openLink" class="w-15">
9
+ <%= spree_time_ago(page.updated_at) %>
10
+ </td>
11
+ <td class="w-10 text-right">
12
+ <%= dropdown do %>
13
+ <%= dropdown_toggle class: 'btn-light btn-sm' do %>
14
+ <%= icon('dots-vertical', class: 'mr-0') %>
15
+ <% end %>
16
+ <%= dropdown_menu do %>
17
+ <ul class="list-none p-0 mb-0">
18
+ <li>
19
+ <%= link_to_with_icon 'edit', Spree.t(:edit), spree.edit_admin_theme_path(current_store.default_theme, page_id: page.id), class: 'dropdown-item', data: { turbo_frame: '_top', turbo_prefetch: false } %>
20
+ </li>
21
+ <li>
22
+ <%= link_to_delete(page, class: 'dropdown-item text-red-600 hover:bg-red-100', icon: 'trash') %>
23
+ </li>
24
+ </ul>
25
+ <% end %>
26
+ <% end %>
27
+ </td>
28
+ </tr>
@@ -0,0 +1,13 @@
1
+ <%= turbo_frame_tag :dialog do %>
2
+ <%= form_with model: @page, url: spree.admin_page_path(@page), data: { controller: 'slug-form seo-form' }, scope: :page do |f| %>
3
+ <%= dialog_header(@page.display_name) %>
4
+ <div class="dialog-body">
5
+ <%= render 'spree/admin/shared/error_messages', target: @page %>
6
+ <%= render 'form', f: f, vertical: true %>
7
+ </div>
8
+ <div class="dialog-footer">
9
+ <%= turbo_save_button_tag Spree.t('actions.save'), data: {turbo_frame: :_top} %>
10
+ <%= link_to Spree.t('actions.destroy'), spree.admin_page_path(@page.id), data: { turbo_method: :delete, turbo_frame: '_top', turbo_confirm: Spree.t(:are_you_sure )}, class: 'btn btn-danger ml-auto' %>
11
+ </div>
12
+ <% end %>
13
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%= search_form_for [:admin, @search], url: spree.admin_pages_path , class: "filter-wrap", data: {controller: "filters"} do |f| %>
2
+ <%= render 'spree/admin/shared/filters_search_bar', param: :name_cont, label: Spree.t(:name) %>
3
+
4
+ <%= render "spree/admin/shared/filter_badge_template" %>
5
+
6
+ <div data-filters-target="badgesContainer" class="filter-badges-container"></div>
7
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:pages) %>
3
+ <% end %>
4
+ <% content_for :page_actions do %>
5
+ <%= render_admin_partials(:pages_actions_partials) %>
6
+ <%= link_to_with_icon 'plus', Spree.t(:add_new_page), spree.new_admin_page_path, class: "btn btn-primary" if can?(:create, Spree::Page.new) %>
7
+ <% end %>
8
+
9
+ <%= render_admin_partials(:pages_header_partials) %>
10
+
11
+ <%= render 'spree/admin/shared/index_table' %>
@@ -0,0 +1 @@
1
+ <%= render 'spree/admin/shared/new_resource', data: { controller: 'slug-form seo-form' } %>
@@ -0,0 +1,89 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:settings) %>
3
+ <% end %>
4
+
5
+ <% content_for :title do %>
6
+ <%= Spree.t(:settings) %>
7
+ <% end %>
8
+
9
+
10
+ <%= content_for(:page_actions) do %>
11
+ <%= turbo_save_button_tag Spree.t('actions.update'), form: "edit_store_#{@store.id}" %>
12
+ <% end %>
13
+
14
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @store }, class: 'mb-12 pb-12' %>
15
+
16
+ <%= form_for @store, url: spree.admin_storefront_path, method: :patch do |f| %>
17
+ <div class="grid grid-cols-12 gap-16">
18
+ <div class="col-span-12 lg:col-span-6 lg:col-start-4">
19
+ <div class="card mb-6">
20
+ <div class="card-header">
21
+ <h5 class="card-title">
22
+ <%= Spree.t(:seo) %>
23
+ </h5>
24
+ </div>
25
+ <div class="card-body">
26
+ <%= f.spree_text_field :seo_title %>
27
+ <%= f.spree_text_area :meta_description %>
28
+ <%= f.spree_check_box :preferred_index_in_search_engines, label: Spree.t(:index_in_search_engines), help: 'Checking this box will allow your site to appear in search engines.' %>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="card mb-6">
33
+ <div class="card-header">
34
+ <h5 class="card-title">
35
+ <%= Spree.t(:security) %>
36
+ </h5>
37
+ </div>
38
+ <div class="card-body" data-controller="reveal" data-reveal-hidden-class="hidden">
39
+ <%= f.spree_check_box :preferred_password_protected, label: Spree.t(:password_protected), help: 'Checking this box will put your storefront behind a password.', data: { action: 'reveal#toggle' } %>
40
+ <div class="hidden" data-reveal-target="item">
41
+ <%= f.spree_text_field :storefront_password, label: Spree.t(:password), help: 'Enter a password to protect your storefront.' %>
42
+ </div>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="card mb-6">
47
+ <div class="card-header">
48
+ <h5 class="card-title">
49
+ <%= Spree.t(:custom_code) %>
50
+ </h5>
51
+ </div>
52
+ <div class="card-body">
53
+ <%= f.spree_text_area :storefront_custom_code_head, rows: 4 %>
54
+ <%= f.spree_text_area :storefront_custom_code_body_start, rows: 4 %>
55
+ <%= f.spree_text_area :storefront_custom_code_body_end, rows: 4 %>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="card mb-6">
60
+ <div class="card-header">
61
+ <h5 class="card-title">
62
+ <%= Spree.t(:images) %>
63
+ </h5>
64
+ </div>
65
+ <div class="card-body">
66
+ <%= f.spree_file_field :favicon_image, width: 120, height: 120, crop: true, label: 'Favicon image', help_bubble: "A favicon is a small image that appears in the tab of your web browser when you visit a website. It is usually a tiny square or rectangle with a unique design or logo that represents the website" %>
67
+
68
+ <%= f.spree_file_field :social_image, width: 1200, height: 630, crop: true, label: 'Preview image', help_bubble: "This image will be used when your store is shared on social media or in search engines." %>
69
+ </div>
70
+ </div>
71
+
72
+ <div class="card mb-6">
73
+ <div class="card-header">
74
+ <h5 class="card-title">
75
+ <%= Spree.t(:social_media) %>
76
+ </h5>
77
+ </div>
78
+ <div class="card-body">
79
+ <div class="alert alert-info">
80
+ Social links will be displayed in the footer of your store
81
+ </div>
82
+ <% Spree::Store::SUPPORTED_SOCIAL_NETWORKS.each do |social| %>
83
+ <%= f.spree_text_field social.to_sym, label: social.capitalize, placeholder: Spree::Store::SOCIAL_NETWORKS_CONFIG[social.to_sym][:input_placeholder] %>
84
+ <% end %>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ <% end %>
@@ -0,0 +1,56 @@
1
+ <tr id="<%= spree_dom_id theme %>">
2
+ <td class="w-70 p-0">
3
+ <%= link_to theme.ready? ? spree.edit_admin_theme_path(theme) : '#', class: 'flex align-items-top p-6', data: { turbo_prefetch: false } do %>
4
+ <div class="w-20">
5
+ <%= render 'spree/admin/themes/theme_preview_image', theme: theme, css_class: 'border rounded-lg w-full' %>
6
+ </div>
7
+
8
+ <div class="flex flex-col ml-6 w-80">
9
+ <strong><%= theme.name %></strong>
10
+ <p class="mb-0 mt-1">
11
+ <span class="text-gray-600">
12
+ <%= Spree.t(:updated_at) %>:
13
+ <%= spree_time_ago(theme.updated_at) %>
14
+ </span>
15
+ </p>
16
+ </div>
17
+ <% end %>
18
+ </td>
19
+ <td class="w-10">
20
+ <% if theme.ready? %>
21
+ <% if theme.default? %>
22
+ <span class="badge badge-success">
23
+ <%= icon('check') %>
24
+ <%= Spree.t(:live) %>
25
+ </span>
26
+ <% else %>
27
+ <span class="badge badge-light">
28
+ <%= Spree.t(:draft) %>
29
+ </span>
30
+ <% end %>
31
+ <% else %>
32
+ <span class="badge badge-light">
33
+ <%= Spree.t(:duplicating) %>
34
+ </span>
35
+ <% end %>
36
+ </td>
37
+ <td class="w-20">
38
+ <% if theme.ready? %>
39
+ <div class="flex justify-end w-full gap-2">
40
+ <%= link_to Spree.t('actions.publish'), publish_admin_theme_path(theme), class: 'btn btn-sm btn-light', data: { turbo_method: :put } unless theme.default? %>
41
+ <%= dropdown do %>
42
+ <%= dropdown_toggle class: 'btn-light btn-sm h-full' do %>
43
+ <%= icon('dots-vertical', class: 'mr-0') %>
44
+ <% end %>
45
+ <%= dropdown_menu do %>
46
+ <%= link_to_edit theme, class: 'dropdown-item', data: { turbo_prefetch: false } %>
47
+ <%= link_to_with_icon 'copy', Spree.t(:clone), spree.clone_admin_theme_path(theme), class: 'dropdown-item', data: { turbo_method: :post } %>
48
+ <% unless theme.default? %>
49
+ <%= link_to_delete(theme, icon: 'trash', class: 'dropdown-item text-red-600 hover:bg-red-100 shadow-none') if can?(:delete, theme) %>
50
+ <% end %>
51
+ <% end %>
52
+ <% end %>
53
+ </div>
54
+ <% end %>
55
+ </td>
56
+ </tr>
@@ -0,0 +1,17 @@
1
+ <% width ||= 120 %>
2
+ <% height ||= 72 %>
3
+ <% css_class ||= '' %>
4
+
5
+ <% if theme.screenshot.attached? && theme.screenshot.variable? %>
6
+ <%= image_tag(spree_image_url(theme.screenshot, width: width, height: height), class: css_class, loading: :lazy) %>
7
+ <% elsif theme.class.preview_image_url.present? %>
8
+ <%= image_tag(
9
+ theme.class.preview_image_url,
10
+ class: css_class,
11
+ loading: :lazy
12
+ ) %>
13
+ <% else %>
14
+ <div class="rounded-md bg-gray-25 text-gray-600 flex items-center justify-center img-fluid" style="height: <%= height %>px">
15
+ <%= icon('camera-off', class: 'text-gray-600 opacity-50') %>
16
+ </div>
17
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <div class="hidden md:block">
2
+ <div class="desktopLiveView" data-page-builder-target="previewContainer">
3
+ <div class="embed-responsive embed-responsive-4by3 bg-transparent">
4
+ <iframe class="embed-responsive-item border border-gray-200 bg-transparent rounded-xl"
5
+ data-page-builder-target="iframe"
6
+ data-action="load->page-builder#initializeVisualEditor"
7
+ id="page-builder-preview"
8
+ sandbox="allow-same-origin allow-forms allow-popups allow-scripts allow-modals">
9
+ </iframe>
10
+ </div>
11
+ </div>
12
+ </div>
13
+
14
+ <%= turbo_frame_tag :iframe_preview_scripts %>
@@ -0,0 +1,80 @@
1
+ <%= content_for :page_title do %>
2
+ <%= Spree.t(:themes) %>
3
+ <% end %>
4
+
5
+ <%= content_for :page_actions do %>
6
+ <%= render_admin_partials(:themes_actions_partials) %>
7
+ <%= link_to_with_icon 'eye', Spree.t(:view_your_store), current_store.formatted_url_or_custom_domain, class: 'btn btn-light', target: '_blank' %>
8
+ <% end %>
9
+
10
+ <%= content_for :page_alerts do %>
11
+ <div class="alert alert-info">
12
+ If you wan't to make changes to theme but don't want to go live with them,
13
+ you can make a copy of the theme and make changes to the copy. And later promote that copy
14
+ to the live theme.
15
+ </div>
16
+ <br />
17
+
18
+ <% unless current_store.default_custom_domain&.active? %>
19
+ <div class="alert alert-info inline-flex items-center py-2 pr-2">
20
+ <div class="flex items-center">
21
+ <span>
22
+ You don't have domain connected to your store
23
+ </span>
24
+ <%= link_to 'Connect your own domain', spree.admin_custom_domains_path, class: 'btn btn-secondary ml-4' %>
25
+ </div>
26
+ </div>
27
+ <% end %>
28
+ <% end %>
29
+
30
+ <%= render_admin_partials(:themes_header_partials) %>
31
+
32
+ <div class="card-lg">
33
+ <table class="table">
34
+ <thead>
35
+ <tr>
36
+ <th><%= Spree.t(:theme) %></th>
37
+ <th><%= Spree.t(:status) %></th>
38
+ <th></th>
39
+ </tr>
40
+ </thead>
41
+ <tbody>
42
+ <%= render partial: 'spree/admin/themes/theme', collection: @themes, cached: spree_base_cache_scope %>
43
+ </tbody>
44
+ </table>
45
+ </div>
46
+
47
+ <% if available_themes.any? %>
48
+ <div class="card-lg">
49
+ <div class="card-header">
50
+ <h5 class="card-title">Add a new theme</h5>
51
+ </div>
52
+ <div class="card-body pb-0">
53
+ <p>Pick a theme from the list of available themes</p>
54
+
55
+ <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3">
56
+ <% available_themes.each do |theme| %>
57
+ <div class="col mb-6">
58
+ <div class="card hover:bg-gray-25">
59
+ <% if theme.preview_image_url.present? %>
60
+ <%= image_tag theme.preview_image_url, class: 'card-img-top' %>
61
+ <% end %>
62
+ <div class="card-body">
63
+ <h5 class="card-title"><%= theme.display_name %></h5>
64
+ <p class="text-gray-600">
65
+ by <%= link_to theme.authors.join(', '), theme.website, target: '_blank' %>
66
+ </p>
67
+ <p><%= theme.description %></p>
68
+
69
+ <%= form_for :theme, url: spree.admin_themes_path do |f| %>
70
+ <%= f.hidden_field :type, value: theme.to_s %>
71
+ <%= f.submit Spree.t(:add), class: 'btn btn-primary' %>
72
+ <% end %>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ <% end %>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= refresh_theme_preview %>
@@ -0,0 +1,46 @@
1
+ # Page Builder Admin Navigation Configuration
2
+ # This file defines the storefront/page builder navigation for Spree Admin
3
+
4
+ Rails.application.config.after_initialize do
5
+ # ===============================================
6
+ # Sidebar Navigation
7
+ # ===============================================
8
+ sidebar_nav = Spree.admin.navigation.sidebar
9
+
10
+ # Storefront with submenu
11
+ sidebar_nav.add :storefront,
12
+ label: 'admin.storefront',
13
+ url: :admin_themes_path,
14
+ icon: 'building-store',
15
+ position: 65,
16
+ if: -> { can?(:manage, Spree::Theme) } do |storefront|
17
+
18
+ # Pages
19
+ storefront.add :pages,
20
+ label: :pages,
21
+ url: :admin_pages_path,
22
+ position: 20,
23
+ if: -> { can?(:manage, Spree::Page) }
24
+
25
+ # Storefront Settings
26
+ storefront.add :storefront_settings,
27
+ label: :settings,
28
+ url: :edit_admin_storefront_path,
29
+ position: 40,
30
+ if: -> { can?(:manage, current_store) }
31
+ end
32
+
33
+ # ===============================================
34
+ # Settings Navigation
35
+ # ===============================================
36
+ settings_nav = Spree.admin.navigation.settings
37
+
38
+ # Checkout
39
+ settings_nav.add :checkout,
40
+ label: :checkout,
41
+ url: -> { spree.edit_admin_store_path(section: 'checkout') },
42
+ icon: 'shopping-cart',
43
+ position: 50,
44
+ active: -> { controller_name == 'stores' && params[:section] == 'checkout' },
45
+ if: -> { can?(:manage, current_store) }
46
+ end
@@ -0,0 +1,3 @@
1
+ Rails.application.config.after_initialize do
2
+ Spree.admin.partials.dashboard_sidebar << 'spree/admin/dashboard/store_preview'
3
+ end
@@ -0,0 +1,42 @@
1
+ en:
2
+ spree:
3
+ admin:
4
+ edit_theme: Edit theme
5
+ page_builder:
6
+ background_color: Background color
7
+ border_bottom_width: Border bottom width
8
+ border_color: Border color
9
+ border_top_width: Border top width
10
+ bottom_padding: Bottom padding
11
+ button_alignment: Button alignment
12
+ button_background_color: Button background color
13
+ button_style: Button style
14
+ button_text: Button's text
15
+ button_text_color: Button text color
16
+ category: Category
17
+ description: Description
18
+ description_alignment: Description alignment
19
+ desktop_image_alignment: Image alignment for desktop
20
+ desktop_logo_height: Logo height (desktop)
21
+ heading: Heading
22
+ heading_alignment: Heading alignment
23
+ heading_size: Heading size
24
+ height: Height
25
+ image_click_hint: Clicking on the image will send the user to the selected page/URL
26
+ left_aligned: Left aligned
27
+ logo_centered: Logo centered
28
+ max_video_width_on_desktop: Max video width on desktop
29
+ maximum_posts_to_show: Maximum posts to show
30
+ maximum_products_to_show: Maximum products to show
31
+ nav_centered: Nav centered
32
+ not_set: Not set
33
+ separated: Separated
34
+ show_more_button: Show explore category button
35
+ show_taxon_image: Show taxon image
36
+ text_color: Text color
37
+ title: Title
38
+ top_padding: Top padding
39
+ use_description_from_admin: Use description from admin
40
+ vertical_alignment: Vertical alignment
41
+ youtube_video_url: Youtube video URL
42
+ default_theme_name: Default Theme
data/config/routes.rb ADDED
@@ -0,0 +1,43 @@
1
+ Spree::Core::Engine.add_routes do
2
+ namespace :admin, path: Spree.admin_path do
3
+ # storefront / page builder
4
+ resource :storefront, only: [:edit, :update], controller: :storefront
5
+ resources :themes, except: [:new, :show] do
6
+ member do
7
+ put :update_with_page
8
+ put :publish
9
+ post :clone
10
+ end
11
+ resources :sections, controller: 'page_sections', only: %i[new create] do
12
+ member do
13
+ patch :move_higher
14
+ patch :move_lower
15
+ end
16
+ end
17
+ end
18
+ resources :pages, except: [:show] do
19
+ resources :sections, controller: 'page_sections', only: %i[new create] do
20
+ member do
21
+ patch :move_higher
22
+ patch :move_lower
23
+ end
24
+ end
25
+ end
26
+ resources :page_sections, only: %i[edit update destroy] do
27
+ member do
28
+ patch :restore_design_settings_to_defaults
29
+ end
30
+
31
+ resources :blocks, controller: 'page_blocks' do
32
+ member do
33
+ patch :move_higher
34
+ patch :move_lower
35
+ end
36
+
37
+ resources :links, controller: 'page_links', only: [:create]
38
+ end
39
+ resources :links, controller: 'page_links', only: [:create]
40
+ end
41
+ resources :page_links, only: [:edit, :update, :destroy]
42
+ end
43
+ end
@@ -0,0 +1,78 @@
1
+ class CreatePageBuilderModels < ActiveRecord::Migration[6.1]
2
+ def change
3
+ if !table_exists?(:spree_themes)
4
+ create_table :spree_themes do |t|
5
+ t.string :name
6
+ t.references :store, null: false, index: true
7
+ t.boolean :default, default: false, null: false
8
+ t.boolean :ready, default: true
9
+ t.string :type, default: 'Spree::Themes::Default', null: false
10
+ t.references :parent
11
+ t.text :preferences
12
+
13
+ t.timestamps
14
+ t.datetime :deleted_at
15
+
16
+ t.index ['deleted_at'], name: 'index_spree_themes_on_deleted_at'
17
+ end
18
+
19
+ create_table :spree_pages do |t|
20
+ t.references :pageable, polymorphic: true, null: false
21
+ t.string :type, null: false
22
+ t.string :slug
23
+ t.string :name, null: false
24
+ t.string :meta_title
25
+ t.string :meta_description
26
+ t.string :meta_keywords
27
+ t.references :parent
28
+ t.text :preferences
29
+
30
+ t.timestamps
31
+ t.datetime :deleted_at
32
+
33
+ t.index ['pageable_id', 'name'], name: 'index_spree_pages_on_pageable_id_and_name'
34
+ t.index ['pageable_id', 'pageable_type', 'type'], name: 'index_spree_pages_on_pageable_id_and_pageable_type_and_type'
35
+ t.index ['pageable_id', 'pageable_type'], name: 'index_spree_pages_on_pageable_id_and_pageable_type'
36
+ end
37
+
38
+ create_table :spree_page_sections do |t|
39
+ t.references :pageable, polymorphic: true, null: false, index: true
40
+ t.string :type, null: false
41
+ t.string :name, null: false
42
+ t.integer :position, default: 1, null: false
43
+ t.integer :page_links_count, default: 0
44
+ t.text :preferences
45
+
46
+ t.timestamps
47
+ t.datetime :deleted_at
48
+
49
+ t.index ['pageable_id', 'pageable_type', 'position'], name: 'index_spree_page_sections_on_pageable_w_position'
50
+ end
51
+
52
+ create_table :spree_page_blocks do |t|
53
+ t.references :section, null: false, index: true
54
+ t.string :name, null: false
55
+ t.integer :position, default: 1, null: false
56
+ t.string :type, null: false
57
+ t.integer :page_links_count, default: 0
58
+ t.text :preferences
59
+
60
+ t.timestamps
61
+ t.datetime :deleted_at
62
+
63
+ t.index ['section_id', 'position'], name: 'index_spree_page_blocks_on_section_w_position'
64
+ end
65
+
66
+ create_table :spree_page_links do |t|
67
+ t.references :parent, polymorphic: true, index: true
68
+ t.references :linkable, polymorphic: true, index: true
69
+ t.string :label
70
+ t.string :url
71
+ t.boolean :open_in_new_tab, default: false
72
+ t.integer :position, default: 1, null: false
73
+
74
+ t.timestamps
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,11 @@
1
+ class RemovePageBuilderIndices < ActiveRecord::Migration[7.2]
2
+ def change
3
+ if index_name_exists?(:spree_pages, 'index_spree_pages_on_pageable_id_and_pageable_type_and_slug')
4
+ remove_index :spree_pages, name: 'index_spree_pages_on_pageable_id_and_pageable_type_and_slug'
5
+ end
6
+
7
+ if index_name_exists?(:spree_themes, 'index_spree_themes_on_store_id_and_default')
8
+ remove_index :spree_themes, name: 'index_spree_themes_on_store_id_and_default'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ class AddMissingPageBuilderIndexes < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_index :spree_themes, %w[store_id default], name: 'index_spree_themes_on_store_id_and_default', if_not_exists: true
4
+ add_index :spree_page_links, %w[parent_type parent_id position], name: 'index_spree_page_links_parent_with_position', if_not_exists: true
5
+ add_index :spree_page_blocks, :deleted_at, if_not_exists: true
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ class AddPageLinksCounterCacheToSpreeStores < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_stores, :page_links_count, :integer, default: 0, null: false
4
+
5
+ Spree::Store.reset_column_information
6
+ Spree::Store.find_each do |store|
7
+ Spree::Store.reset_counters(store.id, :links)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module PageBuilder
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ class_option :migrate, type: :boolean, default: true
8
+
9
+ def add_migrations
10
+ run 'bundle exec rake railties:install:migrations FROM=spree_page_builder'
11
+ end
12
+
13
+ def run_migrations
14
+ if options[:migrate]
15
+ run 'bin/rails db:migrate'
16
+ else
17
+ puts 'Skipping rails db:migrate, don\'t forget to run it!'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end