panda-cms 0.7.0

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 (233) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +73 -0
  3. data/Rakefile +7 -0
  4. data/app/assets/builds/panda.cms.css +2808 -0
  5. data/app/assets/config/panda_cms_manifest.js +4 -0
  6. data/app/assets/stylesheets/panda/cms/application.tailwind.css +162 -0
  7. data/app/assets/stylesheets/panda/cms/editor.css +120 -0
  8. data/app/builders/panda/cms/form_builder.rb +234 -0
  9. data/app/components/panda/cms/admin/button_component.rb +70 -0
  10. data/app/components/panda/cms/admin/container_component.html.erb +13 -0
  11. data/app/components/panda/cms/admin/container_component.rb +13 -0
  12. data/app/components/panda/cms/admin/flash_message_component.html.erb +31 -0
  13. data/app/components/panda/cms/admin/flash_message_component.rb +47 -0
  14. data/app/components/panda/cms/admin/heading_component.rb +45 -0
  15. data/app/components/panda/cms/admin/panel_component.html.erb +7 -0
  16. data/app/components/panda/cms/admin/panel_component.rb +13 -0
  17. data/app/components/panda/cms/admin/slideover_component.html.erb +9 -0
  18. data/app/components/panda/cms/admin/slideover_component.rb +15 -0
  19. data/app/components/panda/cms/admin/statistics_component.html.erb +4 -0
  20. data/app/components/panda/cms/admin/statistics_component.rb +17 -0
  21. data/app/components/panda/cms/admin/tab_bar_component.html.erb +35 -0
  22. data/app/components/panda/cms/admin/tab_bar_component.rb +15 -0
  23. data/app/components/panda/cms/admin/table_component.html.erb +29 -0
  24. data/app/components/panda/cms/admin/table_component.rb +46 -0
  25. data/app/components/panda/cms/admin/tag_component.rb +35 -0
  26. data/app/components/panda/cms/admin/user_activity_component.html.erb +5 -0
  27. data/app/components/panda/cms/admin/user_activity_component.rb +33 -0
  28. data/app/components/panda/cms/admin/user_display_component.html.erb +17 -0
  29. data/app/components/panda/cms/admin/user_display_component.rb +21 -0
  30. data/app/components/panda/cms/code_component.rb +64 -0
  31. data/app/components/panda/cms/grid_component.html.erb +6 -0
  32. data/app/components/panda/cms/grid_component.rb +15 -0
  33. data/app/components/panda/cms/menu_component.html.erb +6 -0
  34. data/app/components/panda/cms/menu_component.rb +58 -0
  35. data/app/components/panda/cms/page_menu_component.html.erb +21 -0
  36. data/app/components/panda/cms/page_menu_component.rb +38 -0
  37. data/app/components/panda/cms/rich_text_component.html.erb +6 -0
  38. data/app/components/panda/cms/rich_text_component.rb +84 -0
  39. data/app/components/panda/cms/text_component.rb +72 -0
  40. data/app/constraints/panda/cms/admin_constraint.rb +18 -0
  41. data/app/controllers/panda/cms/admin/block_contents_controller.rb +52 -0
  42. data/app/controllers/panda/cms/admin/dashboard_controller.rb +20 -0
  43. data/app/controllers/panda/cms/admin/files_controller.rb +21 -0
  44. data/app/controllers/panda/cms/admin/forms_controller.rb +53 -0
  45. data/app/controllers/panda/cms/admin/menus_controller.rb +30 -0
  46. data/app/controllers/panda/cms/admin/pages_controller.rb +91 -0
  47. data/app/controllers/panda/cms/admin/posts_controller.rb +146 -0
  48. data/app/controllers/panda/cms/admin/sessions_controller.rb +94 -0
  49. data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +37 -0
  50. data/app/controllers/panda/cms/admin/settings_controller.rb +20 -0
  51. data/app/controllers/panda/cms/application_controller.rb +57 -0
  52. data/app/controllers/panda/cms/errors_controller.rb +33 -0
  53. data/app/controllers/panda/cms/form_submissions_controller.rb +23 -0
  54. data/app/controllers/panda/cms/pages_controller.rb +72 -0
  55. data/app/controllers/panda/cms/posts_controller.rb +13 -0
  56. data/app/helpers/panda/cms/admin/files_helper.rb +6 -0
  57. data/app/helpers/panda/cms/admin/pages_helper.rb +6 -0
  58. data/app/helpers/panda/cms/admin/posts_helper.rb +48 -0
  59. data/app/helpers/panda/cms/application_helper.rb +120 -0
  60. data/app/helpers/panda/cms/pages_helper.rb +6 -0
  61. data/app/helpers/panda/cms/theme_helper.rb +18 -0
  62. data/app/javascript/panda/cms/@editorjs--editorjs.js +2577 -0
  63. data/app/javascript/panda/cms/@hotwired--stimulus.js +4 -0
  64. data/app/javascript/panda/cms/@hotwired--turbo.js +160 -0
  65. data/app/javascript/panda/cms/@rails--actioncable--src.js +4 -0
  66. data/app/javascript/panda/cms/application_panda_cms.js +39 -0
  67. data/app/javascript/panda/cms/controllers/dashboard_controller.js +7 -0
  68. data/app/javascript/panda/cms/controllers/editor_form_controller.js +77 -0
  69. data/app/javascript/panda/cms/controllers/editor_iframe_controller.js +320 -0
  70. data/app/javascript/panda/cms/controllers/index.js +48 -0
  71. data/app/javascript/panda/cms/controllers/slug_controller.js +87 -0
  72. data/app/javascript/panda/cms/editor/css_extractor.js +80 -0
  73. data/app/javascript/panda/cms/editor/editor_js_config.js +177 -0
  74. data/app/javascript/panda/cms/editor/editor_js_initializer.js +285 -0
  75. data/app/javascript/panda/cms/editor/plain_text_editor.js +110 -0
  76. data/app/javascript/panda/cms/editor/resource_loader.js +115 -0
  77. data/app/javascript/panda/cms/tailwindcss-stimulus-components.js +4 -0
  78. data/app/jobs/panda/cms/application_job.rb +6 -0
  79. data/app/jobs/panda/cms/record_visit_job.rb +31 -0
  80. data/app/mailers/panda/cms/application_mailer.rb +8 -0
  81. data/app/mailers/panda/cms/form_mailer.rb +21 -0
  82. data/app/models/action_text/rich_text_version.rb +6 -0
  83. data/app/models/panda/cms/application_record.rb +7 -0
  84. data/app/models/panda/cms/block.rb +34 -0
  85. data/app/models/panda/cms/block_content.rb +18 -0
  86. data/app/models/panda/cms/block_content_version.rb +8 -0
  87. data/app/models/panda/cms/breadcrumb.rb +12 -0
  88. data/app/models/panda/cms/current.rb +17 -0
  89. data/app/models/panda/cms/form.rb +9 -0
  90. data/app/models/panda/cms/form_submission.rb +7 -0
  91. data/app/models/panda/cms/menu.rb +52 -0
  92. data/app/models/panda/cms/menu_item.rb +58 -0
  93. data/app/models/panda/cms/page.rb +96 -0
  94. data/app/models/panda/cms/page_version.rb +8 -0
  95. data/app/models/panda/cms/post.rb +60 -0
  96. data/app/models/panda/cms/post_version.rb +8 -0
  97. data/app/models/panda/cms/redirect.rb +11 -0
  98. data/app/models/panda/cms/template.rb +124 -0
  99. data/app/models/panda/cms/template_version.rb +8 -0
  100. data/app/models/panda/cms/user.rb +31 -0
  101. data/app/models/panda/cms/version.rb +8 -0
  102. data/app/models/panda/cms/visit.rb +9 -0
  103. data/app/services/panda/cms/html_to_editor_js_converter.rb +200 -0
  104. data/app/views/active_storage/blobs/blobs/_blob.html.erb +14 -0
  105. data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
  106. data/app/views/layouts/panda/cms/application.html.erb +41 -0
  107. data/app/views/layouts/panda/cms/public.html.erb +3 -0
  108. data/app/views/panda/cms/admin/dashboard/show.html.erb +12 -0
  109. data/app/views/panda/cms/admin/files/index.html.erb +124 -0
  110. data/app/views/panda/cms/admin/files/show.html.erb +2 -0
  111. data/app/views/panda/cms/admin/forms/edit.html.erb +0 -0
  112. data/app/views/panda/cms/admin/forms/index.html.erb +13 -0
  113. data/app/views/panda/cms/admin/forms/new.html.erb +15 -0
  114. data/app/views/panda/cms/admin/forms/show.html.erb +35 -0
  115. data/app/views/panda/cms/admin/menus/index.html.erb +8 -0
  116. data/app/views/panda/cms/admin/pages/edit.html.erb +36 -0
  117. data/app/views/panda/cms/admin/pages/index.html.erb +22 -0
  118. data/app/views/panda/cms/admin/pages/new.html.erb +15 -0
  119. data/app/views/panda/cms/admin/pages/show.html.erb +1 -0
  120. data/app/views/panda/cms/admin/posts/_form.html.erb +29 -0
  121. data/app/views/panda/cms/admin/posts/edit.html.erb +6 -0
  122. data/app/views/panda/cms/admin/posts/index.html.erb +18 -0
  123. data/app/views/panda/cms/admin/posts/new.html.erb +6 -0
  124. data/app/views/panda/cms/admin/sessions/new.html.erb +17 -0
  125. data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +68 -0
  126. data/app/views/panda/cms/admin/settings/index.html.erb +21 -0
  127. data/app/views/panda/cms/admin/settings/insta.html +4 -0
  128. data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +28 -0
  129. data/app/views/panda/cms/admin/shared/_flash.html.erb +5 -0
  130. data/app/views/panda/cms/admin/shared/_sidebar.html.erb +41 -0
  131. data/app/views/panda/cms/form_mailer/notification_email.html.erb +11 -0
  132. data/app/views/panda/cms/shared/_editor.html.erb +0 -0
  133. data/app/views/panda/cms/shared/_favicons.html.erb +9 -0
  134. data/app/views/panda/cms/shared/_footer.html.erb +2 -0
  135. data/app/views/panda/cms/shared/_header.html.erb +15 -0
  136. data/app/views/panda/cms/shared/_importmap.html.erb +33 -0
  137. data/config/importmap.rb +13 -0
  138. data/config/initializers/inflections.rb +3 -0
  139. data/config/initializers/panda/cms/form_errors.rb +38 -0
  140. data/config/initializers/panda/cms/healthcheck_log_silencer.rb +11 -0
  141. data/config/initializers/panda/cms/paper_trail.rb +7 -0
  142. data/config/initializers/panda/cms.rb +10 -0
  143. data/config/initializers/zeitwork.rb +3 -0
  144. data/config/locales/en.yml +49 -0
  145. data/config/puma/test.rb +9 -0
  146. data/config/routes.rb +48 -0
  147. data/config/tailwind.config.js +37 -0
  148. data/db/migrate/20240205223709_create_panda_cms_pages.rb +9 -0
  149. data/db/migrate/20240219213327_create_panda_cms_page_versions.rb +14 -0
  150. data/db/migrate/20240303002805_create_panda_cms_templates.rb +11 -0
  151. data/db/migrate/20240303003434_create_panda_cms_template_versions.rb +14 -0
  152. data/db/migrate/20240303022441_create_panda_cms_blocks.rb +13 -0
  153. data/db/migrate/20240303024256_create_panda_cms_block_contents.rb +10 -0
  154. data/db/migrate/20240303024746_create_panda_cms_block_content_versions.rb +14 -0
  155. data/db/migrate/20240303233238_add_panda_cms_menu_table.rb +10 -0
  156. data/db/migrate/20240303234724_add_panda_cms_menu_item_table.rb +12 -0
  157. data/db/migrate/20240304134343_add_parent_id_to_panda_cms_pages.rb +5 -0
  158. data/db/migrate/20240305000000_convert_html_content_to_editor_js.rb +82 -0
  159. data/db/migrate/20240315125411_add_status_to_panda_cms_pages.rb +9 -0
  160. data/db/migrate/20240315125421_add_nested_sets_to_panda_cms_pages.rb +16 -0
  161. data/db/migrate/20240316212822_add_kind_to_panda_cms_menus.rb +6 -0
  162. data/db/migrate/20240316221425_add_start_page_to_panda_cms_menus.rb +5 -0
  163. data/db/migrate/20240316230706_add_nested_to_panda_cms_menu_items.rb +24 -0
  164. data/db/migrate/20240317010532_create_panda_cms_users.rb +12 -0
  165. data/db/migrate/20240317161534_add_max_uses_to_panda_cms_template.rb +7 -0
  166. data/db/migrate/20240317163053_reset_counter_cache_on_panda_cms_template.rb +5 -0
  167. data/db/migrate/20240317214827_create_panda_cms_redirects.rb +14 -0
  168. data/db/migrate/20240317230622_create_panda_cms_visits.rb +13 -0
  169. data/db/migrate/20240324205703_create_active_storage_tables.active_storage.rb +58 -0
  170. data/db/migrate/20240408084718_default_panda_cms_users_admin_to_false.rb +5 -0
  171. data/db/migrate/20240701225422_add_service_name_to_active_storage_blobs.active_storage.rb +22 -0
  172. data/db/migrate/20240701225423_create_active_storage_variant_records.active_storage.rb +28 -0
  173. data/db/migrate/20240701225424_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb +8 -0
  174. data/db/migrate/20240804235210_create_panda_cms_forms.rb +11 -0
  175. data/db/migrate/20240805013612_create_panda_cms_form_submissions.rb +9 -0
  176. data/db/migrate/20240805121123_create_panda_cms_posts.rb +27 -0
  177. data/db/migrate/20240805123104_create_panda_cms_post_versions.rb +14 -0
  178. data/db/migrate/20240806112735_fix_panda_cms_visits_column_names.rb +13 -0
  179. data/db/migrate/20240806204412_add_completion_path_to_panda_cms_forms.rb +5 -0
  180. data/db/migrate/20240820081917_change_form_submissions_to_submission_count.rb +5 -0
  181. data/db/migrate/20240904200605_create_action_text_tables.action_text.rb +24 -0
  182. data/db/migrate/20240923234535_add_depth_to_panda_cms_menus.rb +11 -0
  183. data/db/migrate/20241031205109_add_cached_content_to_panda_cms_block_contents.rb +5 -0
  184. data/db/migrate/20241119214548_convert_post_content_to_editor_js.rb +35 -0
  185. data/db/migrate/20241119214549_remove_action_text_from_posts.rb +9 -0
  186. data/db/migrate/20241120000419_remove_post_tag_references.rb +19 -0
  187. data/db/migrate/20241120110943_add_editor_js_to_posts.rb +27 -0
  188. data/db/migrate/20241120113859_add_cached_content_to_panda_cms_posts.rb +5 -0
  189. data/db/migrate/20241123234140_remove_post_tag_id_from_posts.rb +5 -0
  190. data/db/migrate/migrate +1 -0
  191. data/db/seeds.rb +5 -0
  192. data/lib/generators/panda/cms/install_generator.rb +29 -0
  193. data/lib/panda/cms/bulk_editor.rb +171 -0
  194. data/lib/panda/cms/demo_site_generator.rb +67 -0
  195. data/lib/panda/cms/editor_js/blocks/alert.rb +34 -0
  196. data/lib/panda/cms/editor_js/blocks/base.rb +33 -0
  197. data/lib/panda/cms/editor_js/blocks/header.rb +15 -0
  198. data/lib/panda/cms/editor_js/blocks/image.rb +36 -0
  199. data/lib/panda/cms/editor_js/blocks/list.rb +32 -0
  200. data/lib/panda/cms/editor_js/blocks/paragraph.rb +15 -0
  201. data/lib/panda/cms/editor_js/blocks/quote.rb +41 -0
  202. data/lib/panda/cms/editor_js/blocks/table.rb +50 -0
  203. data/lib/panda/cms/editor_js/renderer.rb +124 -0
  204. data/lib/panda/cms/editor_js.rb +16 -0
  205. data/lib/panda/cms/editor_js_content.rb +21 -0
  206. data/lib/panda/cms/engine.rb +257 -0
  207. data/lib/panda/cms/exceptions_app.rb +26 -0
  208. data/lib/panda/cms/railtie.rb +11 -0
  209. data/lib/panda/cms/slug.rb +24 -0
  210. data/lib/panda/cms.rb +0 -0
  211. data/lib/panda-cms/version.rb +5 -0
  212. data/lib/panda-cms.rb +81 -0
  213. data/lib/tasks/panda_cms.rake +54 -0
  214. data/lib/templates/erb/scaffold/_form.html.erb.tt +43 -0
  215. data/lib/templates/erb/scaffold/edit.html.erb.tt +8 -0
  216. data/lib/templates/erb/scaffold/index.html.erb.tt +14 -0
  217. data/lib/templates/erb/scaffold/new.html.erb.tt +7 -0
  218. data/lib/templates/erb/scaffold/partial.html.erb.tt +22 -0
  219. data/lib/templates/erb/scaffold/show.html.erb.tt +15 -0
  220. data/public/panda-cms-assets/favicons/android-chrome-192x192.png +0 -0
  221. data/public/panda-cms-assets/favicons/android-chrome-512x512.png +0 -0
  222. data/public/panda-cms-assets/favicons/apple-touch-icon.png +0 -0
  223. data/public/panda-cms-assets/favicons/browserconfig.xml +9 -0
  224. data/public/panda-cms-assets/favicons/favicon-16x16.png +0 -0
  225. data/public/panda-cms-assets/favicons/favicon-32x32.png +0 -0
  226. data/public/panda-cms-assets/favicons/favicon.ico +0 -0
  227. data/public/panda-cms-assets/favicons/mstile-150x150.png +0 -0
  228. data/public/panda-cms-assets/favicons/safari-pinned-tab.svg +61 -0
  229. data/public/panda-cms-assets/favicons/site.webmanifest +14 -0
  230. data/public/panda-cms-assets/panda-logo-screenprint.png +0 -0
  231. data/public/panda-cms-assets/panda-nav.png +0 -0
  232. data/public/panda-cms-assets/rich_text_editor.css +568 -0
  233. metadata +654 -0
@@ -0,0 +1,29 @@
1
+ <%= panda_cms_form_with model: post, url: url, method: post.persisted? ? :put : :post do |f| %>
2
+ <%= f.text_field :title, class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
3
+ <%= f.text_field :slug, class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
4
+ <%= f.select :user_id, Panda::CMS::User.admin.map { |u| [u.name, u.id] }, {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
5
+ <%= f.datetime_field :published_at, class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
6
+ <%= f.select :status, options_for_select([["Active", "active"], ["Draft", "draft"], ["Hidden", "hidden"], ["Archived", "archived"]], selected: post.status), {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
7
+
8
+ <% editor_id = "editor_#{dom_id(post, :content)}" %>
9
+ <div data-controller="editor-form"
10
+ data-editor-form-editor-id-value="<%= editor_id %>">
11
+ <%= f.hidden_field :content,
12
+ data: {
13
+ editor_form_target: "hiddenField",
14
+ initial_content: editor_content_for(post, local_assigns[:preserved_content]),
15
+ action: "change->editor-form#handleContentChange"
16
+ } %>
17
+ <div id="<%= editor_id %>"
18
+ data-editor-form-target="editorContainer"
19
+ class="max-w-full block ml-12 bg-white pt-1 mb-4 mt-2 border border-mid rounded-md min-h-[300px]">
20
+ </div>
21
+ </div>
22
+
23
+ <%= f.submit post.persisted? ? "Update Post" : "Create Post",
24
+ class: "btn btn-primary",
25
+ data: {
26
+ disable_with: "Saving...",
27
+ action: "click->editor-form#submit"
28
+ } %>
29
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
2
+ <% component.with_heading(text: "Edit Post", level: 1) do |heading| %>
3
+ <% end %>
4
+
5
+ <%= render "form", post: post, url: url, method: :patch %>
6
+ <% end %>
@@ -0,0 +1,18 @@
1
+ <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
2
+ <% component.with_heading(text: "Posts", level: 1) do |heading| %>
3
+ <% heading.with_button(action: :add, text: "Add Post", link: new_admin_post_path) %>
4
+ <% end %>
5
+
6
+ <%= render Panda::CMS::Admin::TableComponent.new(term: "post", rows: posts) do |table| %>
7
+ <% table.column("Title") do |post| %>
8
+ <div class="">
9
+ <%= link_to post.title, edit_admin_post_path(post.admin_param), class: "block h-full w-full" %>
10
+ <span class="block text-xs text-black/60"><%= post_path(post) %></span>
11
+ </div>
12
+ <% end %>
13
+ <% table.column("Status") { |post| render Panda::CMS::Admin::TagComponent.new(status: post.status) } %>
14
+ <% table.column("Published") { |post| render Panda::CMS::Admin::UserActivityComponent.new(at: post.published_at, user: post.user)} %>
15
+ <% table.column("Last Updated") { |post| render Panda::CMS::Admin::UserActivityComponent.new(whodunnit_to: post)} %>
16
+ <% end %>
17
+
18
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
2
+ <% component.with_heading(text: "Add Post", level: 1) do |heading| %>
3
+ <% end %>
4
+
5
+ <%= render "form", post: post, url: url, method: :post %>
6
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <div class="flex flex-col justify-center py-12 px-6 min-h-full text-center lg:px-8">
2
+ <div class="text-center sm:mx-auto sm:w-full sm:max-w-sm">
3
+ <img src="/panda-cms-assets/panda-nav.png" class="py-2 mx-auto w-auto h-32">
4
+ <h2 class="mt-10 mb-6 text-2xl font-bold text-center text-white"><%= t("panda.cms.admin.sessions.new.title") %></h2>
5
+ </div>
6
+ <% @providers.each do |provider| %>
7
+ <div class="mt-4 text-center sm:mx-auto sm:w-full sm:max-w-sm">
8
+ <%= form_tag "#{Panda::CMS.root_path}/auth/#{provider}", method: "post", data: {turbo: false} do %>
9
+ <input type="hidden" name="redirect_uri" value="<%= admin_login_callback_url(provider: provider) %>">
10
+ <button type="submit" id="button-sign-in-<%= provider %>" class="inline-flex gap-x-2 items-center py-2.5 px-3.5 mx-auto mb-4 bg-white rounded-md border min-w-56 border-neutral-400">
11
+ <i class="fa-brands fa-<%= provider %> text-xl mr-1"></i>
12
+ <%= t("panda.cms.authentication.sign_in_with", provider: t("panda.cms.authentication.providers.#{provider}")) %>
13
+ </button>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
17
+ </div>
@@ -0,0 +1,68 @@
1
+ <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
2
+ <% component.with_heading(text: "Bulk Editor", level: 1) %>
3
+
4
+ <% if @debug && @debug[:error].any? %>
5
+ <div class="p-4 mb-4 bg-red-50 rounded-md">
6
+ <div class="flex">
7
+ <div class="flex-shrink-0">
8
+ <i class="text-xl text-red-600 fa-sharp-duotone fa-solid fa-circle-exclamation"></i>
9
+ </div>
10
+ <div class="ml-3">
11
+ <h3 class="text-base font-medium text-red-700">Errors:</h3>
12
+ <div class="mt-2 text-sm text-red-700">
13
+ <ul role="list" class="pl-5 list-disc">
14
+ <% @debug[:error].each do |error| %>
15
+ <li><%= error %></li>
16
+ <% end %>
17
+ </ul>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ <% end %>
23
+
24
+ <% if @debug && @debug[:warning].any? %>
25
+ <div class="p-4 mb-4 bg-yellow-50 rounded-md">
26
+ <div class="flex">
27
+ <div class="flex-shrink-0">
28
+ <i class="text-xl text-yellow-600 fa-duotone fa-solid fa-triangle-exclamation"></i>
29
+ </div>
30
+ <div class="ml-3">
31
+ <h3 class="text-base font-medium text-yellow-700">Warnings:</h3>
32
+ <div class="mt-2 text-sm text-yellow-700">
33
+ <ul role="list" class="pl-5 list-disc">
34
+ <% @debug[:warning].each do |warning| %>
35
+ <li><%= warning %></li>
36
+ <% end %>
37
+ </ul>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ <% end %>
43
+
44
+ <% if @debug && @debug[:success].any? %>
45
+ <div class="p-4 mb-4 bg-green-50 rounded-md">
46
+ <div class="flex">
47
+ <div class="flex-shrink-0">
48
+ <i class="text-xl text-green-600 fa-duotone fa-solid fa-circle-check"></i>
49
+ </div>
50
+ <div class="ml-3">
51
+ <h3 class="text-base font-medium text-green-700">Updates:</h3>
52
+ <div class="mt-2 text-sm text-green-700">
53
+ <ul role="list" class="pl-5 list-disc">
54
+ <% @debug[:success].each do |success| %>
55
+ <li><%= success %></li>
56
+ <% end %>
57
+ </ul>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ <% end %>
63
+
64
+ <%= panda_cms_form_with method: :post do |form| %>
65
+ <%= form.text_area :site_content, value: @json_data, cols: 70, rows: 10, class: "block font-mono w-full" %>
66
+ <%= form.button "Update" %>
67
+ <% end %>
68
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <%= render Panda::CMS::Admin::ContainerComponent.new do |component| %>
2
+ <% component.with_heading(text: "Settings", level: 1) %>
3
+
4
+ <%= render Panda::CMS::Admin::PanelComponent.new do |panel| %>
5
+ <% panel.with_heading(text: "System Status") %>
6
+
7
+ <p class="text-base leading-loose"><i class="mr-2 text-active fa fa-check-circle"></i> <span class="font-medium">Panda CMS:</span> v<%= Panda::CMS::VERSION %></p>
8
+ <p class="text-base leading-loose"><i class="mr-2 text-active fa fa-check-circle"></i> <span class="font-medium">Rails:</span> v<%= Rails.version %></p>
9
+ <p class="text-base leading-loose"><i class="mr-2 text-active fa fa-check-circle"></i> <span class="font-medium">Ruby:</span> v<%= RUBY_VERSION %></p>
10
+ <p class="text-base leading-loose"><i class="mr-2 text-active fa fa-check-circle"></i> <span class="font-medium">Environment:</span> <%= Rails.env.titleize %></p>
11
+ <% end %>
12
+
13
+ <%= render Panda::CMS::Admin::PanelComponent.new do |panel| %>
14
+ <% panel.with_heading(text: "Integrations") %>
15
+ <% end %>
16
+
17
+ <div class="text-center mt-6 space-y-2">
18
+ <p class="text-sm font-semibold">🐼 Panda CMS version: <%= Panda::CMS::VERSION %></p>
19
+ <p class="text-sm">&copy; <%= Date.current.year %> Panda Software Limited. All rights reserved.</p>
20
+ </div>
21
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <%= render Panda::CMS::Admin::ButtonComponent.new( text: "Connect Instagram
2
+ Feed", link:
3
+ "https://api.instagram.com/oauth/authorize?client_id=525993713271219&redirect_uri=https://localhost:3000/admin/integrations/instagram/callback&scope=user_profile,user_media&response_type=code&state=#{current_user.id}",
4
+ icon: "brands fa-instagram" ) %>
@@ -0,0 +1,28 @@
1
+ <% breadcrumb_styles = "text-black/60 hover:text-black/80" %>
2
+
3
+ <div class="flex mt-1 -mb-1">
4
+ <nav aria-label="Breadcrumb" id="panda-breadcrumbs" class="grow">
5
+ <ol role="list" class="px-4 w-full sm:px-6">
6
+ <li class="inline-block">
7
+ <a href="/admin" class="<%= breadcrumb_styles %>">
8
+ <i class="fa fa-regular fa-house"></i>
9
+ <span class="sr-only">Home</span>
10
+ </a>
11
+ </li>
12
+ <% breadcrumbs.each do |crumb| %>
13
+ <li class="inline-block">
14
+ <i class="fa fa-regular fa-chevron-right font-light <%= breadcrumb_styles %> py-5 px-2"></i>
15
+ <a href="<%= crumb.path %>" class="text-sm font-normal <%= breadcrumb_styles %>"><%= crumb.name %></a>
16
+ </li>
17
+ <% end %>
18
+ </ol>
19
+ </nav>
20
+
21
+ <% if content_for :sidebar %>
22
+ <div class="pt-4 pr-8 text-black/80" tabindex="-1">
23
+ <a href="#" id="slideover-toggle" data-action="click->toggle#toggle touch->toggle#toggle" class="text-sm font-light">
24
+ <i class="mr-1 fa-light fa-gear"></i> <%= yield :sidebar_title %>
25
+ </a>
26
+ </div>
27
+ <% end %>
28
+ </div>
@@ -0,0 +1,5 @@
1
+ <% if flash.any? %>
2
+ <% flash.each do |kind, message| %>
3
+ <%= render Panda::CMS::Admin::FlashMessageComponent.new(kind: kind, message: message) %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,41 @@
1
+ <nav class="flex flex-col flex-1">
2
+ <ul role="list" class="flex flex-col flex-1">
3
+ <a class="block p-0 mt-4 mb-4 ml-2 text-xl font-medium text-white"><%= Panda::CMS.config.title %></a>
4
+ <% [
5
+ { path: admin_dashboard_path, text: "Dashboard", icon: "fa-house", matcher: :exact },
6
+ { path: admin_pages_path, text: "Pages", icon: "fa-page", matcher: :starts_with },
7
+ { path: admin_posts_path, text: "Posts", icon: "fa-notebook", matcher: :starts_with },
8
+ { path: admin_forms_path, text: "Forms", icon: "fa-cabinet-filing", matcher: :starts_with },
9
+ { path: admin_menus_path, text: "Menus", icon: "fa-list-dropdown", matcher: :starts_with },
10
+ { path: admin_settings_path, text: "Settings", icon: "fa-cog", matcher: :starts_with },
11
+ ].each do |link| %>
12
+ <li>
13
+ <%= link_to link[:path], class: "#{active_link?(link[:path], match: link[:matcher]) ? selected_nav_highlight_colour_classes(request) : nav_highlight_colour_classes(request)}" do %>
14
+ <span class="text-center"><i class="fa-regular <%= link[:icon] %> text-xl fa-fw"></i></span>
15
+ <span class="mt-1"><%= link[:text] %></span>
16
+ <% end %>
17
+ </li>
18
+ <% end %>
19
+ <li>
20
+ <%= button_to admin_logout_path, method: :delete, id: "logout-link", data: { turbo: false }, class: nav_highlight_colour_classes(request) + " w-full" do %>
21
+ <span class="text-center"><i class="text-xl fa-regular fa-door-open fa-fw"></i></span>
22
+ <span class="mt-1">Logout</span>
23
+ <% end %>
24
+ </li>
25
+ <li class="mt-auto">
26
+ <%= button_to '#', data: { turbo: false }, class: nav_highlight_colour_classes(request) + " w-full" do %>
27
+ <% if !current_user.image_url.to_s.empty? %>
28
+ <span class="text-center"><img src="<%= current_user.image_url %>" class="w-auto h-7 rounded-full"></span>
29
+ <% else %>
30
+ <span class="text-center"><i class="text-xl fa-regular fa-circle-user fa-fw"></i></span>
31
+ <% end %>
32
+ <span class="mt-0.5"><%= current_user.firstname %> <%= current_user.lastname %></span>
33
+ <% end %>
34
+ </li>
35
+ <li class="">
36
+ <img src="/panda-cms-assets/panda-nav.png" class="inline py-2 mr-1 ml-0.5 w-auto h-14">
37
+ <span class="text-xs text-white">v<%= Panda::CMS::VERSION %> | <span class="font-semibold">License:</span> Perpetual</span>
38
+ <a class="absolute right-0 mt-4 mr-5 text-lg text-white lg:hidden" href="#" data-action="click->panda-cms-menu#toggle touch->panda-cms-menu#toggle"><i class="mr-2 text-white fa-sharp fa-solid fa-bars"></i> Menu</a>
39
+ </li>
40
+ </ul>
41
+ </nav>
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
5
+ </head>
6
+ <body>
7
+ <% @submission_data.each do |field, value| %>
8
+ <p><strong><%= field %>:</strong> <%= value %></p>
9
+ <% end %>
10
+ </body>
11
+ </html>
File without changes
@@ -0,0 +1,9 @@
1
+ <link rel="apple-touch-icon" sizes="180x180" href="/panda-cms-assets/favicons/apple-touch-icon.png">
2
+ <link rel="icon" type="image/png" sizes="32x32" href="/panda-cms-assets/favicons/favicon-32x32.png">
3
+ <link rel="icon" type="image/png" sizes="16x16" href="/panda-cms-assets/favicons/favicon-16x16.png">
4
+ <link rel="manifest" href="/panda-cms-assets/favicons/site.webmanifest">
5
+ <link rel="mask-icon" href="/panda-cms-assets/favicons/safari-pinned-tab.svg" color="#5bbad5">
6
+ <link rel="shortcut icon" href="/panda-cms-assets/favicons/favicon.ico">
7
+ <meta name="msapplication-TileColor" content="#b91d47">
8
+ <meta name="msapplication-config" content="/panda-cms-assets/favicons/browserconfig.xml">
9
+ <meta name="theme-color" content="#ffffff">
@@ -0,0 +1,2 @@
1
+ </body>
2
+ </html>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html data-theme="default" class="<%= html_class ||= "" %>">
3
+ <head>
4
+ <title><%= title_tag %></title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+ <script src="https://kit.fontawesome.com/7835d81e75.js" defer="true" crossorigin="anonymous"></script>
8
+ <script async src="https://ga.jspm.io/npm:es-module-shims@1.10.0/dist/es-module-shims.js" defer="true" crossorigin="anonymous"></script>
9
+ <%= stylesheet_link_tag "panda.cms", "data-turbo-track": "reload", media: "all" %>
10
+ <%= render "panda/cms/shared/importmap" %>
11
+ <%= render "panda/cms/shared/favicons" %>
12
+ <%= yield :head %>
13
+ </head>
14
+ <body class="overflow-hidden h-full">
15
+ <%= render "panda/cms/admin/shared/flash" %>
@@ -0,0 +1,33 @@
1
+ <script type="importmap" data-turbo-track="reload">
2
+ <%=
3
+ # This ended up being the best solution to not drag in the importmap from the host app when we're editing the site
4
+ JSON.pretty_generate({
5
+ imports: {
6
+ # Main loader
7
+ "application_panda_cms": asset_path("panda/cms/application_panda_cms.js"),
8
+ # Vendored
9
+ "@hotwired/turbo": asset_path("panda/cms/@hotwired--turbo.js"),
10
+ "@hotwired/stimulus": asset_path("panda/cms/@hotwired--stimulus.js"),
11
+ "@hotwired/stimulus-loading": asset_path("stimulus-loading.js"),
12
+ "@editorjs/editorjs": asset_path("panda/cms/@editorjs--editorjs.js"),
13
+ "tailwindcss-stimulus-components": asset_path("panda/cms/tailwindcss-stimulus-components.js"),
14
+ # Our page editor
15
+ "panda/cms/editor/plain_text_editor": asset_path("panda/cms/editor/plain_text_editor.js"),
16
+ "panda/cms/editor/resource_loader": asset_path("panda/cms/editor/resource_loader.js"),
17
+ "panda/cms/editor/editor_js_initializer": asset_path("panda/cms/editor/editor_js_initializer.js"),
18
+ "panda/cms/editor/editor_js_config": asset_path("panda/cms/editor/editor_js_config.js"),
19
+ "panda/cms/editor/css_extractor": asset_path("panda/cms/editor/css_extractor.js"),
20
+ # Our controllers
21
+ "panda/cms/controllers/dashboard_controller": asset_path("panda/cms/controllers/dashboard_controller.js"),
22
+ "panda/cms/controllers/slug_controller": asset_path("panda/cms/controllers/slug_controller.js"),
23
+ "panda/cms/controllers/editor_form_controller": asset_path("panda/cms/controllers/editor_form_controller.js"),
24
+ "panda/cms/controllers/editor_iframe_controller": asset_path("panda/cms/controllers/editor_iframe_controller.js"),
25
+ # Main controller loader
26
+ "controllers": asset_path("panda/cms/controllers/index.js"),
27
+ }
28
+ }).html_safe
29
+ %>
30
+ </script>
31
+ <script type="module">
32
+ import "application_panda_cms"
33
+ </script>
@@ -0,0 +1,13 @@
1
+ pin "application_panda_cms", to: "panda_cms/application_panda_cms.js", preload: true
2
+
3
+ pin "@hotwired/turbo", to: "@hotwired--turbo.js", preload: true # @8.0.12
4
+ pin "@rails/actioncable/src", to: "@rails--actioncable--src.js", preload: true # @7.2.101
5
+ pin "@hotwired/stimulus", to: "@hotwired--stimulus.js" # @3.2.2
6
+ pin "@hotwired/stimulus-loading", to: "panda_cms/stimulus-loading.js", preload: true
7
+ pin "tailwindcss-stimulus-components" # @6.1.2
8
+ pin "@editorjs/editorjs", to: "panda/cms/editor/editorjs.js" # @2.30.6
9
+
10
+ # Pin the controllers directory
11
+ pin "controllers", to: "panda/cms/controllers/index.js"
12
+ pin_all_from Panda::CMS::Engine.root.join("app/javascript/panda/cms/controllers"), under: "controllers"
13
+ pin_all_from Panda::CMS::Engine.root.join("app/javascript/panda/cms/editor"), under: "editor"
@@ -0,0 +1,3 @@
1
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
2
+ inflect.acronym "CMS"
3
+ end
@@ -0,0 +1,38 @@
1
+ ActionView::Base.field_error_proc = proc do |html_tag, instance|
2
+ html = ""
3
+ form_fields = %w[input select textarea trix-editor label].join(", ")
4
+ error_class = "text-error bg-error border-error border-1 box-shadow-error focus:ring-error focus:border-error "
5
+ message_class = "block w-full text-base p-0 m-0 mt-1 text-error font-semibold"
6
+ autofocused = false
7
+
8
+ Nokogiri::HTML::DocumentFragment.parse(html_tag).css(form_fields).each do |element|
9
+ if form_fields.include?(element.node_name)
10
+ if !autofocused
11
+ # element.attribute("autofocus", "true")
12
+ autofocused = true
13
+ end
14
+
15
+ message = "#{instance.object.class.human_attribute_name(instance.send(:sanitized_method_name))} "
16
+ message += if instance.error_message.respond_to?(:each)
17
+ "#{instance.error_message.uniq.to_sentence}."
18
+ else
19
+ "#{instance.error_message}."
20
+ end
21
+
22
+ if element.node_name.eql?("label")
23
+ html = element.to_s
24
+ else
25
+ element.add_class(error_class)
26
+ html = if element.get_attribute("data-prefix")
27
+ "#{element}</div><div class=\"#{message_class}\">#{message}"
28
+ elsif element.get_attribute("type") != "checkbox"
29
+ "#{element}<div class=\"#{message_class}\">#{message}</div>"
30
+ else
31
+ element.to_s
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ html.html_safe
38
+ end
@@ -0,0 +1,11 @@
1
+ require "silencer/rails/logger"
2
+
3
+ # Don't log requests to the healthcheck endpoint
4
+ Rails.application.configure do
5
+ config.middleware.swap(
6
+ Rails::Rack::Logger,
7
+ Silencer::Logger,
8
+ config.log_tags,
9
+ silence: ["/up"]
10
+ )
11
+ end
@@ -0,0 +1,7 @@
1
+ ActiveSupport.on_load(:action_text_rich_text) do
2
+ ActionText::RichText.class_eval do
3
+ has_paper_trail versions: {
4
+ class_name: "ActionText::RichTextVersion"
5
+ }
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ Panda::CMS.configure do |config|
2
+ # The main title of your website
3
+ config.title = "Demo Site"
4
+
5
+ # The path to the administration panel
6
+ config.admin_path = "/admin"
7
+
8
+ # Site access control
9
+ config.require_login_to_view = false
10
+ end
@@ -0,0 +1,3 @@
1
+ Rails.autoloaders.main.inflector.inflect(
2
+ "panda-cms" => "Panda::CMS"
3
+ )
@@ -0,0 +1,49 @@
1
+ en:
2
+ panda:
3
+ cms:
4
+ admin:
5
+ pages:
6
+ new:
7
+ path:
8
+ meta: "This will be the URL of the page. It should be unique and not contain spaces or special characters. If you're unsure, it'll be auto-generated for you. 🐼"
9
+ sessions:
10
+ create:
11
+ error: There was an error logging you in. Please check your login details and try again, or contact support.
12
+ success: You are logged in!
13
+ destroy:
14
+ success: You have been logged out!
15
+ new:
16
+ title: Sign in to your account
17
+ authentication:
18
+ sign_in_with: Sign in with %{provider}
19
+ providers:
20
+ github: GitHub
21
+ google: Google
22
+ microsoft: Microsoft 365
23
+ activerecord:
24
+ models:
25
+ panda/cms/page: Page
26
+ panda/cms/post: Post
27
+ attributes:
28
+ panda/cms/page:
29
+ title: Title
30
+ path: URL
31
+ panda_cms_template_id: Template
32
+ panda/cms/post:
33
+ title: Title
34
+ slug: URL
35
+ content: Content
36
+ user_id: Author
37
+ published_at: Published At
38
+ post_content: Content
39
+ statuses:
40
+ active: Active
41
+ draft: Draft
42
+ archived: Archived
43
+ hidden: Hidden
44
+ panda/cms/menu:
45
+ name: Menu Name
46
+ panda/cms/menu_item:
47
+ text: Menu Item
48
+ external_url: External URL
49
+ panda/cms_page_id: Page
@@ -0,0 +1,9 @@
1
+ # Puma configuration for test environment
2
+ workers 0
3
+ threads 1, 1
4
+
5
+ port ENV.fetch("PORT") { 3001 }
6
+ environment "test"
7
+
8
+ # Allow for proper shutdown
9
+ plugin :tmp_restart
data/config/routes.rb ADDED
@@ -0,0 +1,48 @@
1
+ require_relative "../app/constraints/panda/cms/admin_constraint"
2
+
3
+ Panda::CMS::Engine.routes.draw do
4
+ constraints Panda::CMS::AdminConstraint.new(&:present?) do
5
+ namespace Panda::CMS.route_namespace, as: :admin, module: :admin do
6
+ resources :files
7
+ resources :forms, only: %i[index show]
8
+ resources :menus
9
+ resources :pages do
10
+ resources :block_contents, only: %i[update]
11
+ end
12
+ resources :posts
13
+
14
+ get "settings", to: "settings#index"
15
+ namespace :settings, as: :settings do
16
+ get "bulk_editor", to: "bulk_editor#new"
17
+ post "bulk_editor", to: "bulk_editor#create"
18
+ end
19
+
20
+ if Rails.env.development?
21
+ mount Lookbook::Engine, at: "/lookbook"
22
+ end
23
+ end
24
+
25
+ get Panda::CMS.route_namespace, to: "admin/dashboard#show", as: :admin_dashboard
26
+ end
27
+
28
+ ### PUBLIC ROUTES ###
29
+
30
+ # Authentication routes
31
+ get Panda::CMS.route_namespace, to: "admin/sessions#new", as: :admin_login
32
+ # Get and post options here are for OmniAuth coming back in, not going out
33
+ match "#{Panda::CMS.route_namespace}/auth/:provider/callback", to: "admin/sessions#create", as: :admin_login_callback, via: %i[get post]
34
+ match "#{Panda::CMS.route_namespace}/auth/failure", to: "admin/sessions#failure", as: :admin_login_failure, via: %i[get post]
35
+ # OmniAuth additionally adds a GET route for "#{Panda::CMS.route_namespace}/auth/:provider" but doesn't name it
36
+ delete Panda::CMS.route_namespace, to: "admin/sessions#destroy", as: :admin_logout
37
+
38
+ if Panda::CMS.config.posts[:enabled]
39
+ # TODO: Allow multiple types of post in future
40
+ # TODO: This now requires a page to be created, make it explicit (with a rendering posts helper?)
41
+ # get Panda::CMS.posts[:prefix], to: "posts#index", as: :posts
42
+ get "#{Panda::CMS.config.posts[:prefix]}/:slug", to: "posts#show", as: :post
43
+ end
44
+
45
+ ### APPENDED ROUTES ###
46
+
47
+ # See lib/panda/cms/engine.rb
48
+ end
@@ -0,0 +1,37 @@
1
+ module.exports = {
2
+ content: {
3
+ relative: true,
4
+ files: [
5
+ "../public/*.html",
6
+ "../app/views/**/*.html.erb",
7
+ "../app/builders/panda/cms/**/*.rb",
8
+ "../app/components/panda/cms/**/*.html.erb",
9
+ "../app/components/panda/cms/**/*.rb",
10
+ "../app/helpers/panda/cms/**/*.rb",
11
+ "../app/javascript/panda/cms/**/*.js",
12
+ "../vendor/javascript/**/*.js",
13
+ ],
14
+ },
15
+ theme: {
16
+ colors: {
17
+ transparent: "transparent",
18
+ current: "currentColor",
19
+ white: "rgb(var(--color-white) / <alpha-value>)",
20
+ black: "rgb(var(--color-black) / <alpha-value>)",
21
+ light: "rgb(var(--color-light) / <alpha-value>)",
22
+ mid: "rgb(var(--color-mid) / <alpha-value>)",
23
+ dark: "rgb(var(--color-dark) / <alpha-value>)",
24
+ highlight: "rgb(var(--color-highlight) / <alpha-value>)",
25
+ active: "rgb(var(--color-active) / <alpha-value>)",
26
+ inactive: "rgb(var(--color-active) / <alpha-value>)",
27
+ warning: "rgb(var(--color-warning) / <alpha-value>)",
28
+ error: "rgb(var(--color-error) / <alpha-value>)",
29
+ },
30
+ },
31
+ plugins: [
32
+ require("@tailwindcss/forms"),
33
+ require("@tailwindcss/aspect-ratio"),
34
+ require("@tailwindcss/typography"),
35
+ require("@tailwindcss/container-queries"),
36
+ ],
37
+ };
@@ -0,0 +1,9 @@
1
+ class CreatePandaCMSPages < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_pages, id: :uuid do |t|
4
+ t.string :title
5
+ t.string :path
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ class CreatePandaCMSPageVersions < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_page_versions, id: :uuid do |t|
4
+ t.string :item_type, null: false
5
+ t.string :item_id, null: false
6
+ t.string :event, null: false
7
+ t.string :whodunnit
8
+ t.jsonb :object
9
+ t.jsonb :object_changes
10
+ t.datetime :created_at
11
+ end
12
+ add_index :panda_cms_page_versions, %i[item_type item_id]
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ class CreatePandaCMSTemplates < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_templates, id: :uuid do |t|
4
+ t.string :name
5
+ t.string :file_path
6
+ t.timestamps
7
+ end
8
+
9
+ add_reference :panda_cms_pages, :panda_cms_template, type: :uuid, foreign_key: {to_table: :panda_cms_templates}, null: false
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ class CreatePandaCMSTemplateVersions < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :panda_cms_template_versions, id: :uuid do |t|
4
+ t.string :item_type, null: false
5
+ t.string :item_id, null: false
6
+ t.string :event, null: false
7
+ t.string :whodunnit
8
+ t.jsonb :object
9
+ t.jsonb :object_changes
10
+ t.datetime :created_at
11
+ end
12
+ add_index :panda_cms_template_versions, %i[item_type item_id]
13
+ end
14
+ end