panda-cms 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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