ruby_cms 0.2.1.0 → 1.0.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 (647) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -72
  3. data/README.md +308 -153
  4. data/Rakefile +0 -18
  5. data/SYNC_RULES.md +84 -0
  6. data/exe/ruby_cms +13 -18
  7. data/lib/generators/ruby_cms/admin_page_generator.rb +116 -60
  8. data/lib/generators/ruby_cms/templates/admin.html.erb +35 -17
  9. data/lib/generators/ruby_cms/templates/admin_page/_admin_table_content.html.erb.tt +25 -0
  10. data/lib/generators/ruby_cms/templates/admin_page/_form.html.erb.tt +36 -0
  11. data/lib/generators/ruby_cms/templates/admin_page/_row.html.erb.tt +20 -0
  12. data/lib/generators/ruby_cms/templates/admin_page/controller.rb.tt +74 -2
  13. data/lib/generators/ruby_cms/templates/admin_page/edit.html.erb.tt +14 -0
  14. data/lib/generators/ruby_cms/templates/admin_page/index.html.erb.tt +21 -10
  15. data/lib/generators/ruby_cms/templates/admin_page/new.html.erb.tt +14 -0
  16. data/lib/generators/ruby_cms/templates/assets/tailwind/application.css +225 -0
  17. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion.rb +17 -0
  18. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_content.rb +21 -0
  19. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_default_content.rb +17 -0
  20. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_default_trigger.rb +19 -0
  21. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_icon.rb +38 -0
  22. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_item.rb +28 -0
  23. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_trigger.rb +17 -0
  24. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_page.rb +31 -0
  25. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_page_header.rb +65 -0
  26. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_resource_card.rb +28 -0
  27. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_table_content.rb +20 -0
  28. data/lib/generators/ruby_cms/templates/components/ruby_ui/alert/alert.rb +36 -0
  29. data/lib/generators/ruby_cms/templates/components/ruby_ui/alert/alert_description.rb +17 -0
  30. data/lib/generators/ruby_cms/templates/components/ruby_ui/alert/alert_title.rb +17 -0
  31. data/lib/generators/ruby_cms/templates/components/ruby_ui/aspect_ratio/aspect_ratio.rb +33 -0
  32. data/lib/generators/ruby_cms/templates/components/ruby_ui/avatar/avatar.rb +31 -0
  33. data/lib/generators/ruby_cms/templates/components/ruby_ui/avatar/avatar_fallback.rb +17 -0
  34. data/lib/generators/ruby_cms/templates/components/ruby_ui/avatar/avatar_image.rb +26 -0
  35. data/lib/generators/ruby_cms/templates/components/ruby_ui/badge/badge.rb +60 -0
  36. data/lib/generators/ruby_cms/templates/components/ruby_ui/base.rb +29 -0
  37. data/lib/generators/ruby_cms/templates/components/ruby_ui/box/box.rb +15 -0
  38. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb.rb +17 -0
  39. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_ellipsis.rb +39 -0
  40. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_item.rb +17 -0
  41. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_link.rb +22 -0
  42. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_list.rb +17 -0
  43. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_page.rb +19 -0
  44. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_separator.rb +38 -0
  45. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_button.rb +82 -0
  46. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table.rb +133 -0
  47. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_actions.rb +104 -0
  48. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_body.rb +17 -0
  49. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_checkbox_cell.rb +40 -0
  50. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_checkbox_head.rb +34 -0
  51. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_delete_modal.rb +43 -0
  52. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_header.rb +42 -0
  53. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_pagination.rb +115 -0
  54. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_row.rb +77 -0
  55. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_actions.rb +86 -0
  56. data/lib/generators/ruby_cms/templates/components/ruby_ui/button/button.rb +113 -0
  57. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar.rb +39 -0
  58. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_body.rb +19 -0
  59. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_days.rb +104 -0
  60. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_header.rb +17 -0
  61. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_next.rb +43 -0
  62. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_prev.rb +43 -0
  63. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_title.rb +27 -0
  64. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_weekdays.rb +33 -0
  65. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card.rb +17 -0
  66. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_content.rb +17 -0
  67. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_description.rb +17 -0
  68. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_footer.rb +17 -0
  69. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_header.rb +17 -0
  70. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_title.rb +17 -0
  71. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel.rb +44 -0
  72. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_content.rb +23 -0
  73. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_item.rb +23 -0
  74. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_next.rb +48 -0
  75. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_previous.rb +49 -0
  76. data/lib/generators/ruby_cms/templates/components/ruby_ui/checkbox/checkbox.rb +29 -0
  77. data/lib/generators/ruby_cms/templates/components/ruby_ui/checkbox/checkbox_group.rb +20 -0
  78. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard.rb +42 -0
  79. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard_popover.rb +40 -0
  80. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard_source.rb +19 -0
  81. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard_trigger.rb +20 -0
  82. data/lib/generators/ruby_cms/templates/components/ruby_ui/codeblock/codeblock.rb +102 -0
  83. data/lib/generators/ruby_cms/templates/components/ruby_ui/collapsible/collapsible.rb +25 -0
  84. data/lib/generators/ruby_cms/templates/components/ruby_ui/collapsible/collapsible_content.rb +18 -0
  85. data/lib/generators/ruby_cms/templates/components/ruby_ui/collapsible/collapsible_trigger.rb +19 -0
  86. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox.rb +26 -0
  87. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_checkbox.rb +25 -0
  88. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_empty_state.rb +21 -0
  89. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_item.rb +25 -0
  90. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_list.rb +18 -0
  91. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_list_group.rb +20 -0
  92. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_popover.rb +30 -0
  93. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_radio.rb +26 -0
  94. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_search_input.rb +53 -0
  95. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +25 -0
  96. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_trigger.rb +57 -0
  97. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command.rb +9 -0
  98. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_dialog.rb +17 -0
  99. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_dialog_content.rb +48 -0
  100. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_dialog_trigger.rb +29 -0
  101. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_empty.rb +19 -0
  102. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_group.rb +40 -0
  103. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_input.rb +56 -0
  104. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_item.rb +32 -0
  105. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_list.rb +17 -0
  106. data/lib/generators/ruby_cms/templates/components/ruby_ui/container/container.rb +33 -0
  107. data/lib/generators/ruby_cms/templates/components/ruby_ui/container/container_section.rb +31 -0
  108. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu.rb +26 -0
  109. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_content.rb +25 -0
  110. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_item.rb +66 -0
  111. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_label.rb +24 -0
  112. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_separator.rb +19 -0
  113. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_trigger.rb +20 -0
  114. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table.rb +29 -0
  115. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_bulk_actions.rb +18 -0
  116. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_column_toggle.rb +62 -0
  117. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_expand_toggle.rb +53 -0
  118. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_form.rb +39 -0
  119. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_kaminari_adapter.rb +17 -0
  120. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_manual_adapter.rb +17 -0
  121. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_pagination.rb +100 -0
  122. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_pagination_bar.rb +15 -0
  123. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_pagy_adapter.rb +17 -0
  124. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_per_page_select.rb +35 -0
  125. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_row_checkbox.rb +30 -0
  126. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_search.rb +57 -0
  127. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_select_all_checkbox.rb +21 -0
  128. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_selection_summary.rb +25 -0
  129. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_sort_head.rb +112 -0
  130. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_toolbar.rb +15 -0
  131. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog.rb +25 -0
  132. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_content.rb +78 -0
  133. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_description.rb +17 -0
  134. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_footer.rb +17 -0
  135. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_header.rb +17 -0
  136. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_middle.rb +17 -0
  137. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_title.rb +17 -0
  138. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_trigger.rb +20 -0
  139. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu.rb +35 -0
  140. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_content.rb +44 -0
  141. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_item.rb +28 -0
  142. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_label.rb +17 -0
  143. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_separator.rb +19 -0
  144. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_trigger.rb +18 -0
  145. data/lib/generators/ruby_cms/templates/components/ruby_ui/empty_state/empty_state.rb +190 -0
  146. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form.rb +15 -0
  147. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field.rb +20 -0
  148. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field_error.rb +20 -0
  149. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field_hint.rb +15 -0
  150. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field_label.rb +15 -0
  151. data/lib/generators/ruby_cms/templates/components/ruby_ui/hover_card/hover_card.rb +27 -0
  152. data/lib/generators/ruby_cms/templates/components/ruby_ui/hover_card/hover_card_content.rb +22 -0
  153. data/lib/generators/ruby_cms/templates/components/ruby_ui/hover_card/hover_card_trigger.rb +20 -0
  154. data/lib/generators/ruby_cms/templates/components/ruby_ui/input/input.rb +34 -0
  155. data/lib/generators/ruby_cms/templates/components/ruby_ui/link/link.rb +106 -0
  156. data/lib/generators/ruby_cms/templates/components/ruby_ui/masked_input/masked_input.rb +15 -0
  157. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal.rb +26 -0
  158. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_action.rb +17 -0
  159. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_cancel.rb +20 -0
  160. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_content.rb +68 -0
  161. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_description.rb +17 -0
  162. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_footer.rb +17 -0
  163. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_header.rb +17 -0
  164. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_title.rb +17 -0
  165. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select.rb +39 -0
  166. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select_group.rb +15 -0
  167. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select_icon.rb +39 -0
  168. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select_option.rb +15 -0
  169. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination.rb +19 -0
  170. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination_content.rb +17 -0
  171. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination_ellipsis.rb +42 -0
  172. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination_item.rb +28 -0
  173. data/lib/generators/ruby_cms/templates/components/ruby_ui/popover/popover.rb +26 -0
  174. data/lib/generators/ruby_cms/templates/components/ruby_ui/popover/popover_content.rb +27 -0
  175. data/lib/generators/ruby_cms/templates/components/ruby_ui/popover/popover_trigger.rb +20 -0
  176. data/lib/generators/ruby_cms/templates/components/ruby_ui/progress/progress.rb +37 -0
  177. data/lib/generators/ruby_cms/templates/components/ruby_ui/radio_button/radio_button.rb +25 -0
  178. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select.rb +23 -0
  179. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_content.rb +32 -0
  180. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_group.rb +15 -0
  181. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_input.rb +22 -0
  182. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_item.rb +52 -0
  183. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_label.rb +17 -0
  184. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_trigger.rb +54 -0
  185. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_value.rb +27 -0
  186. data/lib/generators/ruby_cms/templates/components/ruby_ui/separator/separator.rb +38 -0
  187. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet.rb +17 -0
  188. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_content.rb +77 -0
  189. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_description.rb +17 -0
  190. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_footer.rb +17 -0
  191. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_header.rb +17 -0
  192. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_middle.rb +17 -0
  193. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_title.rb +17 -0
  194. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_trigger.rb +17 -0
  195. data/lib/generators/ruby_cms/templates/components/ruby_ui/shortcut_key/shortcut_key.rb +17 -0
  196. data/lib/generators/ruby_cms/templates/components/ruby_ui/skeleton/skeleton.rb +17 -0
  197. data/lib/generators/ruby_cms/templates/components/ruby_ui/switch/switch.rb +24 -0
  198. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table.rb +19 -0
  199. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_body.rb +17 -0
  200. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_caption.rb +17 -0
  201. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_cell.rb +17 -0
  202. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_footer.rb +17 -0
  203. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_head.rb +17 -0
  204. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_header.rb +17 -0
  205. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_row.rb +17 -0
  206. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs.rb +25 -0
  207. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs_content.rb +26 -0
  208. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs_list.rb +17 -0
  209. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs_trigger.rb +28 -0
  210. data/lib/generators/ruby_cms/templates/components/ruby_ui/textarea/textarea.rb +26 -0
  211. data/lib/generators/ruby_cms/templates/components/ruby_ui/theme_toggle/set_dark_mode.rb +16 -0
  212. data/lib/generators/ruby_cms/templates/components/ruby_ui/theme_toggle/set_light_mode.rb +16 -0
  213. data/lib/generators/ruby_cms/templates/components/ruby_ui/theme_toggle/theme_toggle.rb +9 -0
  214. data/lib/generators/ruby_cms/templates/components/ruby_ui/tooltip/tooltip.rb +26 -0
  215. data/lib/generators/ruby_cms/templates/components/ruby_ui/tooltip/tooltip_content.rb +26 -0
  216. data/lib/generators/ruby_cms/templates/components/ruby_ui/tooltip/tooltip_trigger.rb +19 -0
  217. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/heading.rb +60 -0
  218. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/inline_code.rb +17 -0
  219. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/inline_link.rb +22 -0
  220. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/text.rb +53 -0
  221. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/typography_blockquote.rb +17 -0
  222. data/lib/generators/ruby_cms/templates/{ruby_cms.rb → config/initializers/admin.rb} +15 -9
  223. data/lib/generators/ruby_cms/templates/config/initializers/admin_dashboard.rb +43 -0
  224. data/lib/generators/ruby_cms/templates/config/initializers/pagy.rb +7 -0
  225. data/lib/generators/ruby_cms/templates/config/initializers/ruby_cms_core.rb +25 -0
  226. data/lib/generators/ruby_cms/templates/config/initializers/ruby_cms_custom_settings.rb +35 -0
  227. data/lib/generators/ruby_cms/templates/config/initializers/webauthn.rb +28 -0
  228. data/lib/generators/ruby_cms/templates/config/locales/en.yml +277 -0
  229. data/lib/generators/ruby_cms/templates/config/locales/nl.yml +329 -0
  230. data/lib/generators/ruby_cms/templates/controllers/admin/analytics_controller.rb +211 -0
  231. data/lib/generators/ruby_cms/templates/controllers/admin/application_controller.rb +186 -0
  232. data/lib/generators/ruby_cms/templates/controllers/admin/audit_log_entries_controller.rb +69 -0
  233. data/lib/generators/ruby_cms/templates/controllers/admin/commands_controller.rb +146 -0
  234. data/lib/generators/ruby_cms/templates/controllers/admin/content_block_versions_controller.rb +96 -0
  235. data/lib/generators/ruby_cms/templates/controllers/admin/content_blocks_controller.rb +405 -0
  236. data/lib/generators/ruby_cms/templates/controllers/admin/dashboard_controller.rb +114 -0
  237. data/lib/generators/ruby_cms/templates/controllers/admin/invitations_controller.rb +64 -0
  238. data/lib/generators/ruby_cms/templates/controllers/admin/locale_controller.rb +19 -0
  239. data/lib/generators/ruby_cms/templates/controllers/admin/media_assets_controller.rb +186 -0
  240. data/lib/generators/ruby_cms/templates/controllers/admin/notifications_controller.rb +37 -0
  241. data/lib/generators/ruby_cms/templates/controllers/admin/passkey_credentials_controller.rb +17 -0
  242. data/lib/generators/ruby_cms/templates/controllers/admin/passkey_registrations_controller.rb +50 -0
  243. data/lib/generators/ruby_cms/templates/controllers/admin/permissions_controller.rb +132 -0
  244. data/lib/generators/ruby_cms/templates/controllers/admin/redirects_controller.rb +175 -0
  245. data/lib/generators/ruby_cms/templates/controllers/admin/security_controller.rb +146 -0
  246. data/lib/generators/ruby_cms/templates/controllers/admin/settings_controller.rb +298 -0
  247. data/lib/generators/ruby_cms/templates/controllers/admin/system_health_controller.rb +11 -0
  248. data/lib/generators/ruby_cms/templates/controllers/admin/trash_controller.rb +61 -0
  249. data/lib/generators/ruby_cms/templates/controllers/admin/user_permissions_controller.rb +91 -0
  250. data/lib/generators/ruby_cms/templates/controllers/admin/users_controller.rb +220 -0
  251. data/lib/generators/ruby_cms/templates/controllers/admin/visitor_errors_controller.rb +114 -0
  252. data/lib/generators/ruby_cms/templates/controllers/admin/visual_editor_controller.rb +384 -0
  253. data/lib/generators/ruby_cms/templates/controllers/concerns/.keep +0 -0
  254. data/lib/generators/ruby_cms/templates/controllers/concerns/admin_bulk_actions.rb +59 -0
  255. data/lib/generators/ruby_cms/templates/controllers/concerns/admin_pagination.rb +106 -0
  256. data/lib/generators/ruby_cms/templates/controllers/concerns/admin_turbo_table.rb +46 -0
  257. data/lib/generators/ruby_cms/templates/controllers/concerns/audit_loggable.rb +28 -0
  258. data/lib/generators/ruby_cms/templates/controllers/concerns/authentication.rb +63 -0
  259. data/lib/generators/ruby_cms/templates/controllers/concerns/page_tracking.rb +45 -0
  260. data/lib/generators/ruby_cms/templates/controllers/concerns/security_monitoring.rb +102 -0
  261. data/lib/generators/ruby_cms/templates/controllers/concerns/sudo_mode.rb +29 -0
  262. data/lib/generators/ruby_cms/templates/controllers/concerns/visitor_error_capture.rb +35 -0
  263. data/lib/generators/ruby_cms/templates/controllers/errors_controller.rb +36 -0
  264. data/lib/generators/ruby_cms/templates/controllers/passkey_sessions_controller.rb +69 -0
  265. data/lib/generators/ruby_cms/templates/db/migrate/20251121085414_create_email_blocklists.rb +12 -0
  266. data/lib/generators/ruby_cms/templates/db/migrate/20251121161828_create_ip_blocklists.rb +12 -0
  267. data/lib/generators/ruby_cms/templates/db/migrate/20251205120000_add_analytics_indices.rb +48 -0
  268. data/lib/generators/ruby_cms/templates/db/migrate/20251216064753_backfill_security_fields_in_ahoy_events.rb +49 -0
  269. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000001_create_ruby_cms_permissions.rb +2 -2
  270. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000002_create_ruby_cms_user_permissions.rb +2 -2
  271. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000003_create_ruby_cms_content_blocks.rb +3 -3
  272. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000010_add_indexes_to_ruby_cms_tables.rb +1 -1
  273. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260127000001_add_locale_to_ruby_cms_content_blocks.rb +8 -6
  274. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260129000001_create_ruby_cms_visitor_errors.rb +4 -4
  275. data/lib/generators/ruby_cms/templates/db/migrate/20260130000001_add_referer_and_query_to_ruby_cms_visitor_errors.rb +10 -0
  276. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260130000002_create_ruby_cms_preferences.rb +2 -2
  277. data/lib/generators/ruby_cms/templates/db/migrate/20260130000003_add_category_to_ruby_cms_preferences.rb +10 -0
  278. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260328000001_create_content_block_versions.rb +2 -2
  279. data/lib/generators/ruby_cms/templates/db/migrate/20260525120000_create_audit_log_entries.rb +24 -0
  280. data/lib/generators/ruby_cms/templates/db/migrate/20260525130000_create_redirects.rb +28 -0
  281. data/lib/generators/ruby_cms/templates/db/migrate/20260525140000_create_media_assets.rb +25 -0
  282. data/lib/generators/ruby_cms/templates/db/migrate/20260525150000_create_invitations.rb +22 -0
  283. data/lib/generators/ruby_cms/templates/db/migrate/20260525160000_create_command_runs.rb +22 -0
  284. data/lib/generators/ruby_cms/templates/db/migrate/20260525190000_add_state_to_visitor_errors.rb +20 -0
  285. data/lib/generators/ruby_cms/templates/db/migrate/20260527165017_add_discarded_at_to_visitor_errors.rb +10 -0
  286. data/lib/generators/ruby_cms/templates/db/migrate/20260527165728_add_discarded_at_to_cms_tables.rb +12 -0
  287. data/lib/generators/ruby_cms/templates/db/migrate/20260605151935_create_passkey_credentials.rb +15 -0
  288. data/lib/generators/ruby_cms/templates/db/migrate/20260605151936_add_webauthn_id_to_users.rb +22 -0
  289. data/lib/generators/ruby_cms/templates/db/migrate/20260606000001_add_authenticated_at_to_sessions.rb +14 -0
  290. data/lib/generators/ruby_cms/templates/helpers/admin/admin_page_helper.rb +9 -0
  291. data/lib/generators/ruby_cms/templates/helpers/admin/dashboard_helper.rb +16 -0
  292. data/lib/generators/ruby_cms/templates/helpers/admin/media_assets_helper.rb +38 -0
  293. data/lib/generators/ruby_cms/templates/helpers/admin/relative_time_helper.rb +37 -0
  294. data/lib/generators/ruby_cms/templates/helpers/admin/ui_helper.rb +48 -0
  295. data/lib/generators/ruby_cms/templates/helpers/cms_application_helpers.rb +70 -0
  296. data/lib/generators/ruby_cms/templates/helpers/content_blocks_helper.rb +382 -0
  297. data/lib/generators/ruby_cms/templates/helpers/settings_helper.rb +192 -0
  298. data/lib/generators/ruby_cms/templates/javascript/admin.js +6 -0
  299. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/admin_commands_controller.js +2 -2
  300. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/autosearch_controller.js +18 -0
  301. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/clickable_row_controller.js +11 -0
  302. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/clipboard_controller.js +34 -0
  303. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/command_palette_controller.js +189 -0
  304. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/commands_controller.js +300 -0
  305. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/content_block_history_controller.js +167 -0
  306. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/flash_messages_controller.js +1 -1
  307. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/inline_rich_editor_controller.js +157 -0
  308. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/media_selection_controller.js +76 -0
  309. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/media_uploader_controller.js +23 -0
  310. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/mobile_menu_controller.js +125 -0
  311. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/nav_order_sortable_controller.js +296 -0
  312. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/nav_shortcuts_controller.js +47 -0
  313. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/page_preview_controller.js +64 -3
  314. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/permission_matrix_controller.js +49 -0
  315. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/search_shortcut_controller.js +24 -0
  316. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/settings_rail_search_controller.js +17 -0
  317. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/shortcuts_overlay_controller.js +39 -0
  318. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/sidebar_collapse_controller.js +135 -0
  319. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/sidebar_nav_persist_controller.js +48 -0
  320. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/sidebar_toggle_trigger_controller.js +10 -0
  321. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/topbar_controller.js +88 -0
  322. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/visual_editor_controller.js +721 -0
  323. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/visual_editor_page_select_controller.js +174 -0
  324. data/lib/generators/ruby_cms/templates/javascript/controllers/application.js +10 -0
  325. data/lib/generators/ruby_cms/templates/javascript/controllers/index.js +2 -0
  326. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/accordion_controller.js +97 -0
  327. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui}/bulk_action_table_controller.js +86 -233
  328. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/calendar_controller.js +249 -0
  329. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/calendar_input_controller.js +8 -0
  330. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/carousel_controller.js +60 -0
  331. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/checkbox_group_controller.js +21 -0
  332. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/clipboard_controller.js +54 -0
  333. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/collapsible_controller.js +47 -0
  334. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/combobox_controller.js +190 -0
  335. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/command_controller.js +138 -0
  336. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/context_menu_controller.js +144 -0
  337. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js +14 -0
  338. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/data_table_controller.js +109 -0
  339. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/data_table_search_controller.js +62 -0
  340. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/dialog_controller.js +32 -0
  341. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/dropdown_menu_controller.js +149 -0
  342. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/form_field_controller.js +61 -0
  343. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/hover_card_controller.js +153 -0
  344. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/masked_input_controller.js +9 -0
  345. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/modal_controller.js +45 -0
  346. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/popover_controller.js +107 -0
  347. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/select_controller.js +192 -0
  348. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/select_item_controller.js +11 -0
  349. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/sheet_content_controller.js +7 -0
  350. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/sheet_controller.js +9 -0
  351. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/tabs_controller.js +67 -0
  352. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/theme_toggle_controller.js +30 -0
  353. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/tooltip_controller.js +38 -0
  354. data/lib/generators/ruby_cms/templates/javascript/controllers/webauthn_controller.js +153 -0
  355. data/lib/generators/ruby_cms/templates/lib/ruby_cms/cli.rb +170 -0
  356. data/lib/generators/ruby_cms/templates/lib/ruby_cms/commands_registry.rb +76 -0
  357. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/content_blocks_grouping.rb +2 -6
  358. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/content_blocks_sync.rb +6 -8
  359. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/dashboard_blocks.rb +5 -5
  360. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/icons.rb +41 -10
  361. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/settings.rb +60 -10
  362. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/settings_registry.rb +79 -3
  363. data/lib/generators/ruby_cms/templates/lib/ruby_cms.rb +342 -0
  364. data/lib/{tasks → generators/ruby_cms/templates/lib/tasks}/admin.rake +16 -16
  365. data/lib/generators/ruby_cms/templates/lib/tasks/ruby_cms.rake +158 -0
  366. data/lib/generators/ruby_cms/templates/models/audit_log_entry.rb +56 -0
  367. data/lib/generators/ruby_cms/templates/models/command_run.rb +29 -0
  368. data/lib/generators/ruby_cms/templates/models/concerns/content_block/searchable.rb +18 -0
  369. data/{app → lib/generators/ruby_cms/templates}/models/concerns/content_block/versionable.rb +1 -1
  370. data/lib/generators/ruby_cms/templates/models/concerns/ransackable.rb +32 -0
  371. data/{app → lib/generators/ruby_cms/templates}/models/content_block.rb +13 -5
  372. data/lib/generators/ruby_cms/templates/models/email_blocklist.rb +21 -0
  373. data/lib/generators/ruby_cms/templates/models/invitation.rb +86 -0
  374. data/lib/generators/ruby_cms/templates/models/ip_blocklist.rb +34 -0
  375. data/lib/generators/ruby_cms/templates/models/media_asset.rb +90 -0
  376. data/lib/generators/ruby_cms/templates/models/passkey_credential.rb +6 -0
  377. data/lib/generators/ruby_cms/templates/models/permission.rb +71 -0
  378. data/lib/generators/ruby_cms/templates/models/permittable.rb +69 -0
  379. data/lib/generators/ruby_cms/templates/models/preference.rb +109 -0
  380. data/lib/generators/ruby_cms/templates/models/redirect.rb +100 -0
  381. data/lib/generators/ruby_cms/templates/models/user_permission.rb +25 -0
  382. data/lib/generators/ruby_cms/templates/models/visitor_error.rb +157 -0
  383. data/lib/generators/ruby_cms/templates/nav/analytics.rb +3 -0
  384. data/lib/generators/ruby_cms/templates/nav/audit_log.rb +3 -0
  385. data/lib/generators/ruby_cms/templates/nav/commands.rb +3 -0
  386. data/lib/generators/ruby_cms/templates/nav/content_blocks.rb +3 -0
  387. data/lib/generators/ruby_cms/templates/nav/core.rb +13 -0
  388. data/lib/generators/ruby_cms/templates/nav/media.rb +3 -0
  389. data/lib/generators/ruby_cms/templates/nav/permissions.rb +3 -0
  390. data/lib/generators/ruby_cms/templates/nav/redirects.rb +3 -0
  391. data/lib/generators/ruby_cms/templates/nav/security.rb +4 -0
  392. data/lib/generators/ruby_cms/templates/nav/system_health.rb +3 -0
  393. data/lib/generators/ruby_cms/templates/nav/trash.rb +3 -0
  394. data/lib/generators/ruby_cms/templates/nav/users.rb +3 -0
  395. data/lib/generators/ruby_cms/templates/nav/visual_editor.rb +3 -0
  396. data/lib/generators/ruby_cms/templates/routes/analytics.rb +5 -0
  397. data/lib/generators/ruby_cms/templates/routes/audit_log.rb +3 -0
  398. data/lib/generators/ruby_cms/templates/routes/auth.public.rb +4 -0
  399. data/lib/generators/ruby_cms/templates/routes/auth.rb +6 -0
  400. data/lib/generators/ruby_cms/templates/routes/commands.rb +4 -0
  401. data/lib/generators/ruby_cms/templates/routes/content_blocks.rb +12 -0
  402. data/lib/generators/ruby_cms/templates/routes/core.public.rb +5 -0
  403. data/lib/generators/ruby_cms/templates/routes/core.rb +16 -0
  404. data/lib/generators/ruby_cms/templates/routes/media.rb +9 -0
  405. data/lib/generators/ruby_cms/templates/routes/passkeys.public.rb +4 -0
  406. data/lib/generators/ruby_cms/templates/routes/passkeys.rb +5 -0
  407. data/lib/generators/ruby_cms/templates/routes/permissions.rb +5 -0
  408. data/lib/generators/ruby_cms/templates/routes/redirects.rb +10 -0
  409. data/lib/generators/ruby_cms/templates/routes/security.rb +20 -0
  410. data/lib/generators/ruby_cms/templates/routes/system_health.rb +3 -0
  411. data/lib/generators/ruby_cms/templates/routes/trash.rb +5 -0
  412. data/lib/generators/ruby_cms/templates/routes/users.rb +10 -0
  413. data/lib/generators/ruby_cms/templates/routes/visual_editor.rb +5 -0
  414. data/lib/generators/ruby_cms/templates/services/admin/health_check.rb +362 -0
  415. data/lib/generators/ruby_cms/templates/services/analytics/report.rb +503 -0
  416. data/lib/generators/ruby_cms/templates/services/analytics_service.rb +305 -0
  417. data/lib/generators/ruby_cms/templates/services/audit_log.rb +60 -0
  418. data/lib/generators/ruby_cms/templates/services/command_runner.rb +79 -0
  419. data/lib/generators/ruby_cms/templates/services/security_service.rb +203 -0
  420. data/lib/generators/ruby_cms/templates/services/security_tracker.rb +90 -0
  421. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/index.html.erb +61 -50
  422. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/page_details.html.erb +15 -10
  423. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_browser_device.html.erb +2 -2
  424. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_conversions.html.erb +3 -3
  425. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_daily_activity_chart.html.erb +18 -17
  426. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_exit_pages.html.erb +4 -4
  427. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_hourly_activity_chart.html.erb +18 -17
  428. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_landing_pages.html.erb +3 -3
  429. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_os_stats.html.erb +2 -2
  430. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_popular_pages.html.erb +4 -4
  431. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_recent_activity.html.erb +4 -4
  432. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_top_referrers.html.erb +3 -3
  433. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_top_visitors.html.erb +4 -4
  434. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_utm_sources.html.erb +2 -2
  435. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/visitor_details.html.erb +15 -10
  436. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/_admin_table_content.html.erb +55 -0
  437. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/_row.html.erb +84 -0
  438. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/index.html.erb +33 -0
  439. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/show.html.erb +89 -0
  440. data/lib/generators/ruby_cms/templates/views/admin/commands/index.html.erb +393 -0
  441. data/lib/generators/ruby_cms/templates/views/admin/content_block_versions/index.html.erb +63 -0
  442. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/content_block_versions/show.html.erb +2 -2
  443. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_audits.html.erb +49 -0
  444. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_form.html.erb +79 -0
  445. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_rich_field.html.erb +44 -0
  446. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_row.html.erb +60 -0
  447. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/index.html.erb +84 -0
  448. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/new.html.erb +18 -0
  449. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/content_blocks/show.html.erb +67 -61
  450. data/lib/generators/ruby_cms/templates/views/admin/dashboard/_gmail_token_status.html.erb +69 -0
  451. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_analytics_overview.html.erb +11 -11
  452. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_content_blocks_stats.html.erb +3 -3
  453. data/lib/generators/ruby_cms/templates/views/admin/dashboard/blocks/_notifications.html.erb +50 -0
  454. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_permissions_stats.html.erb +3 -3
  455. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_quick_actions.html.erb +15 -15
  456. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_recent_errors.html.erb +8 -8
  457. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_users_stats.html.erb +3 -3
  458. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_visitor_errors_stats.html.erb +3 -3
  459. data/lib/generators/ruby_cms/templates/views/admin/dashboard/index.html.erb +34 -0
  460. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_admin_table_content.html.erb +107 -0
  461. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_form.html.erb +33 -0
  462. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_grid.html.erb +58 -0
  463. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_list.html.erb +43 -0
  464. data/lib/generators/ruby_cms/templates/views/admin/media_assets/edit.html.erb +13 -0
  465. data/lib/generators/ruby_cms/templates/views/admin/media_assets/index.html.erb +36 -0
  466. data/lib/generators/ruby_cms/templates/views/admin/media_assets/show.html.erb +97 -0
  467. data/lib/generators/ruby_cms/templates/views/admin/passkey_credentials/index.html.erb +30 -0
  468. data/lib/generators/ruby_cms/templates/views/admin/passkey_registrations/new.html.erb +23 -0
  469. data/lib/generators/ruby_cms/templates/views/admin/permissions/_admin_table_content.html.erb +45 -0
  470. data/lib/generators/ruby_cms/templates/views/admin/permissions/_matrix.html.erb +75 -0
  471. data/lib/generators/ruby_cms/templates/views/admin/permissions/_row.html.erb +62 -0
  472. data/lib/generators/ruby_cms/templates/views/admin/permissions/index.html.erb +65 -0
  473. data/lib/generators/ruby_cms/templates/views/admin/redirects/_admin_table_content.html.erb +77 -0
  474. data/lib/generators/ruby_cms/templates/views/admin/redirects/_form.html.erb +97 -0
  475. data/lib/generators/ruby_cms/templates/views/admin/redirects/_row.html.erb +82 -0
  476. data/lib/generators/ruby_cms/templates/views/admin/redirects/edit.html.erb +21 -0
  477. data/lib/generators/ruby_cms/templates/views/admin/redirects/index.html.erb +40 -0
  478. data/lib/generators/ruby_cms/templates/views/admin/redirects/new.html.erb +12 -0
  479. data/lib/generators/ruby_cms/templates/views/admin/security/index.html.erb +88 -0
  480. data/lib/generators/ruby_cms/templates/views/admin/security/ip_blocklist_content.html.erb +3 -0
  481. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_events_by_type.html.erb +24 -0
  482. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_ip_blocklist.html.erb +37 -0
  483. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_ip_blocklist_content.html.erb +41 -0
  484. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_ip_management.html.erb +11 -0
  485. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_recent_events.html.erb +59 -0
  486. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_top_threat_ips.html.erb +21 -0
  487. data/lib/generators/ruby_cms/templates/views/admin/settings/index.html.erb +509 -0
  488. data/lib/generators/ruby_cms/templates/views/admin/shared/_add_button.html.erb +15 -0
  489. data/lib/generators/ruby_cms/templates/views/admin/shared/_data_table.html.erb +255 -0
  490. data/lib/generators/ruby_cms/templates/views/admin/shared/_turbo_pagination.html.erb +53 -0
  491. data/lib/generators/ruby_cms/templates/views/admin/system_health/index.html.erb +248 -0
  492. data/lib/generators/ruby_cms/templates/views/admin/trash/index.html.erb +58 -0
  493. data/lib/generators/ruby_cms/templates/views/admin/user_permissions/_row.html.erb +29 -0
  494. data/lib/generators/ruby_cms/templates/views/admin/user_permissions/index.html.erb +78 -0
  495. data/lib/generators/ruby_cms/templates/views/admin/users/_admin_table_content.html.erb +50 -0
  496. data/lib/generators/ruby_cms/templates/views/admin/users/_invite_form.html.erb +43 -0
  497. data/lib/generators/ruby_cms/templates/views/admin/users/_invites_panel.html.erb +91 -0
  498. data/lib/generators/ruby_cms/templates/views/admin/users/_row.html.erb +109 -0
  499. data/lib/generators/ruby_cms/templates/views/admin/users/index.html.erb +43 -0
  500. data/lib/generators/ruby_cms/templates/views/admin/users/show.html.erb +343 -0
  501. data/lib/generators/ruby_cms/templates/views/admin/visitor_errors/_admin_table_content.html.erb +61 -0
  502. data/lib/generators/ruby_cms/templates/views/admin/visitor_errors/_row.html.erb +90 -0
  503. data/lib/generators/ruby_cms/templates/views/admin/visitor_errors/index.html.erb +18 -0
  504. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/visitor_errors/show.html.erb +4 -4
  505. data/lib/generators/ruby_cms/templates/views/admin/visual_editor/index.html.erb +383 -0
  506. data/{app/views/layouts/ruby_cms → lib/generators/ruby_cms/templates/views/layouts/admin}/_admin_flash_messages.html.erb +3 -3
  507. data/lib/generators/ruby_cms/templates/views/layouts/admin/_admin_sidebar.html.erb +239 -0
  508. data/lib/generators/ruby_cms/templates/views/layouts/admin/_command_palette.html.erb +65 -0
  509. data/lib/generators/ruby_cms/templates/views/layouts/admin/_shortcuts_overlay.html.erb +81 -0
  510. data/lib/generators/ruby_cms/templates/views/layouts/admin/_topbar_widgets.html.erb +9 -0
  511. data/lib/generators/ruby_cms/templates/views/layouts/admin/minimal.html.erb +71 -0
  512. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_bell.html.erb +81 -0
  513. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_profile_menu.html.erb +86 -0
  514. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_search_trigger.html.erb +9 -0
  515. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_shortcuts_button.html.erb +8 -0
  516. data/lib/generators/ruby_cms/templates/views/shared/_maintenance_banner.html.erb +10 -0
  517. data/lib/ruby_cms/app_wiring.rb +56 -0
  518. data/lib/ruby_cms/auth_wiring.rb +64 -0
  519. data/lib/ruby_cms/cli.rb +181 -121
  520. data/lib/ruby_cms/excludes.rb +41 -0
  521. data/lib/ruby_cms/file_installer.rb +41 -0
  522. data/lib/ruby_cms/gem_setup.rb +84 -0
  523. data/lib/ruby_cms/helper_wiring.rb +32 -0
  524. data/lib/ruby_cms/importmap_wiring.rb +41 -0
  525. data/lib/ruby_cms/installer.rb +128 -0
  526. data/lib/ruby_cms/lockfile.rb +49 -0
  527. data/lib/ruby_cms/manifest.rb +88 -0
  528. data/lib/ruby_cms/manifest_data.rb +233 -0
  529. data/lib/ruby_cms/migration_installer.rb +64 -0
  530. data/lib/ruby_cms/nav_assembler.rb +43 -0
  531. data/lib/ruby_cms/passkey_wiring.rb +42 -0
  532. data/lib/ruby_cms/path_map.rb +26 -0
  533. data/lib/ruby_cms/routes_assembler.rb +103 -0
  534. data/lib/ruby_cms/syncer.rb +81 -0
  535. data/lib/ruby_cms/updater.rb +76 -0
  536. data/lib/ruby_cms/version.rb +1 -1
  537. data/lib/ruby_cms.rb +4 -245
  538. metadata +606 -169
  539. data/.cursor/dhh.mdc +0 -698
  540. data/app/components/ruby_cms/admin/admin_page/admin_table_content.rb +0 -32
  541. data/app/components/ruby_cms/admin/admin_page.rb +0 -345
  542. data/app/components/ruby_cms/admin/admin_page_header.rb +0 -79
  543. data/app/components/ruby_cms/admin/admin_resource_card.rb +0 -55
  544. data/app/components/ruby_cms/admin/base_component.rb +0 -78
  545. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table.rb +0 -157
  546. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_actions.rb +0 -127
  547. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_body.rb +0 -15
  548. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_cell.rb +0 -43
  549. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_head.rb +0 -35
  550. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_delete_modal.rb +0 -174
  551. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header.rb +0 -59
  552. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header_bar.rb +0 -169
  553. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_pagination.rb +0 -192
  554. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_row.rb +0 -96
  555. data/app/components/ruby_cms/admin/bulk_action_table/bulk_actions.rb +0 -196
  556. data/app/controllers/concerns/ruby_cms/admin_pagination.rb +0 -120
  557. data/app/controllers/concerns/ruby_cms/admin_turbo_table.rb +0 -68
  558. data/app/controllers/concerns/ruby_cms/page_tracking.rb +0 -67
  559. data/app/controllers/concerns/ruby_cms/visitor_error_capture.rb +0 -39
  560. data/app/controllers/ruby_cms/admin/analytics_controller.rb +0 -213
  561. data/app/controllers/ruby_cms/admin/base_controller.rb +0 -132
  562. data/app/controllers/ruby_cms/admin/commands_controller.rb +0 -122
  563. data/app/controllers/ruby_cms/admin/content_block_versions_controller.rb +0 -62
  564. data/app/controllers/ruby_cms/admin/content_blocks_controller.rb +0 -391
  565. data/app/controllers/ruby_cms/admin/dashboard_controller.rb +0 -89
  566. data/app/controllers/ruby_cms/admin/locale_controller.rb +0 -21
  567. data/app/controllers/ruby_cms/admin/permissions_controller.rb +0 -66
  568. data/app/controllers/ruby_cms/admin/settings_controller.rb +0 -223
  569. data/app/controllers/ruby_cms/admin/user_permissions_controller.rb +0 -77
  570. data/app/controllers/ruby_cms/admin/users_controller.rb +0 -107
  571. data/app/controllers/ruby_cms/admin/visitor_errors_controller.rb +0 -89
  572. data/app/controllers/ruby_cms/admin/visual_editor_controller.rb +0 -345
  573. data/app/controllers/ruby_cms/errors_controller.rb +0 -35
  574. data/app/helpers/ruby_cms/admin/admin_page_helper.rb +0 -21
  575. data/app/helpers/ruby_cms/admin/bulk_action_table_helper.rb +0 -159
  576. data/app/helpers/ruby_cms/admin/dashboard_helper.rb +0 -20
  577. data/app/helpers/ruby_cms/application_helper.rb +0 -41
  578. data/app/helpers/ruby_cms/bulk_action_table_helper.rb +0 -151
  579. data/app/helpers/ruby_cms/content_blocks_helper.rb +0 -399
  580. data/app/helpers/ruby_cms/settings_helper.rb +0 -172
  581. data/app/javascript/controllers/ruby_cms/content_block_history_controller.js +0 -91
  582. data/app/javascript/controllers/ruby_cms/index.js +0 -117
  583. data/app/javascript/controllers/ruby_cms/mobile_menu_controller.js +0 -55
  584. data/app/javascript/controllers/ruby_cms/nav_order_sortable_controller.js +0 -192
  585. data/app/javascript/controllers/ruby_cms/visual_editor_controller.js +0 -325
  586. data/app/models/concerns/content_block/searchable.rb +0 -22
  587. data/app/models/ruby_cms/content_block.rb +0 -8
  588. data/app/models/ruby_cms/permission.rb +0 -57
  589. data/app/models/ruby_cms/permittable.rb +0 -37
  590. data/app/models/ruby_cms/preference.rb +0 -111
  591. data/app/models/ruby_cms/user_permission.rb +0 -12
  592. data/app/models/ruby_cms/visitor_error.rb +0 -109
  593. data/app/services/ruby_cms/analytics/report.rb +0 -512
  594. data/app/services/ruby_cms/command_runner.rb +0 -42
  595. data/app/services/ruby_cms/security_tracker.rb +0 -92
  596. data/app/views/admin/content_block_versions/index.html.erb +0 -52
  597. data/app/views/admin/content_block_versions/show.html.erb +0 -37
  598. data/app/views/layouts/ruby_cms/_admin_sidebar.html.erb +0 -122
  599. data/app/views/layouts/ruby_cms/admin.html.erb +0 -77
  600. data/app/views/layouts/ruby_cms/minimal.html.erb +0 -181
  601. data/app/views/ruby_cms/admin/commands/index.html.erb +0 -104
  602. data/app/views/ruby_cms/admin/content_block_versions/index.html.erb +0 -52
  603. data/app/views/ruby_cms/admin/content_blocks/_form.html.erb +0 -92
  604. data/app/views/ruby_cms/admin/content_blocks/_row.html.erb +0 -25
  605. data/app/views/ruby_cms/admin/content_blocks/index.html.erb +0 -62
  606. data/app/views/ruby_cms/admin/content_blocks/new.html.erb +0 -10
  607. data/app/views/ruby_cms/admin/dashboard/index.html.erb +0 -31
  608. data/app/views/ruby_cms/admin/permissions/_row.html.erb +0 -11
  609. data/app/views/ruby_cms/admin/permissions/index.html.erb +0 -62
  610. data/app/views/ruby_cms/admin/settings/index.html.erb +0 -268
  611. data/app/views/ruby_cms/admin/shared/_bulk_action_table_index.html.erb +0 -56
  612. data/app/views/ruby_cms/admin/user_permissions/index.html.erb +0 -85
  613. data/app/views/ruby_cms/admin/users/_row.html.erb +0 -17
  614. data/app/views/ruby_cms/admin/users/index.html.erb +0 -70
  615. data/app/views/ruby_cms/admin/visitor_errors/_row.html.erb +0 -35
  616. data/app/views/ruby_cms/admin/visitor_errors/index.html.erb +0 -57
  617. data/app/views/ruby_cms/admin/visual_editor/index.html.erb +0 -157
  618. data/app/views/ruby_cms/errors/not_found.html.erb +0 -92
  619. data/config/database.yml +0 -6
  620. data/config/importmap.rb +0 -40
  621. data/config/locales/en.yml +0 -198
  622. data/config/locales/nl.yml +0 -156
  623. data/config/routes.rb +0 -76
  624. data/db/migrate/20260130000001_add_referer_and_query_to_ruby_cms_visitor_errors.rb +0 -8
  625. data/db/migrate/20260130000003_add_category_to_ruby_cms_preferences.rb +0 -8
  626. data/docs/assets/ruby_cms_logo.png +0 -0
  627. data/lib/generators/ruby_cms/install_generator.rb +0 -1080
  628. data/lib/ruby_cms/app_integration.rb +0 -82
  629. data/lib/ruby_cms/commands_registry.rb +0 -40
  630. data/lib/ruby_cms/css_compiler.rb +0 -35
  631. data/lib/ruby_cms/engine/admin_permissions.rb +0 -69
  632. data/lib/ruby_cms/engine/content_blocks_tasks.rb +0 -66
  633. data/lib/ruby_cms/engine/css.rb +0 -14
  634. data/lib/ruby_cms/engine/dashboard_registration.rb +0 -66
  635. data/lib/ruby_cms/engine/navigation_registration.rb +0 -89
  636. data/lib/ruby_cms/engine.rb +0 -273
  637. /data/{app → lib/generators/ruby_cms/templates}/assets/images/ruby_cms/logo.png +0 -0
  638. /data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260211000001_add_ruby_cms_analytics_fields_to_ahoy_events.rb +0 -0
  639. /data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260212000001_use_unprefixed_cms_tables.rb +0 -0
  640. /data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260409000001_add_analytics_performance_indexes.rb +0 -0
  641. /data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/auto_save_preference_controller.js +0 -0
  642. /data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/locale_tabs_controller.js +0 -0
  643. /data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/toggle_controller.js +0 -0
  644. /data/{app → lib/generators/ruby_cms/templates}/models/concerns/content_block/publishable.rb +0 -0
  645. /data/{app → lib/generators/ruby_cms/templates}/models/content_block_version.rb +0 -0
  646. /data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_back_button.html.erb +0 -0
  647. /data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_security_alert.html.erb +0 -0
data/Rakefile CHANGED
@@ -9,22 +9,4 @@ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
- # CSS compile (no Rails needed - for gem development)
13
- namespace :ruby_cms do
14
- namespace :css do
15
- desc "Compile RubyCMS admin.css from component files (for gem development)"
16
- task compile_gem: :environment do
17
- require_relative "lib/ruby_cms/css_compiler"
18
- gem_root = __dir__
19
- dest = File.join(gem_root, "app/assets/stylesheets/ruby_cms/admin.css")
20
- RubyCms::CssCompiler.compile(gem_root, dest)
21
- puts "✓ Compiled admin.css in gem"
22
- end
23
- end
24
- end
25
-
26
- task :environment do
27
- # No-op for gem Rakefile; Rails app Rakefiles load full env
28
- end
29
-
30
12
  task default: %i[spec rubocop]
data/SYNC_RULES.md ADDED
@@ -0,0 +1,84 @@
1
+ # RubyCMS sync rules
2
+
3
+ The gem `ruby_cms` is **install-only**: its templates under
4
+ `lib/generators/ruby_cms/templates/` are copied verbatim into host apps. Two
5
+ apps consume gem-owned code and must stay in sync with the gem:
6
+
7
+ - `gvexcelsior` (`../gvexcelsior`)
8
+ - `portfolio` (`../portfolio`)
9
+
10
+ **Golden rule:** the gem template is the source of truth for any gem-owned file.
11
+ A change to a gem-owned file is not done until it lives in the gem templates
12
+ **and** both apps carry the same version.
13
+
14
+ ## Direction of flow
15
+
16
+ | Direction | Command (run from app root) | What it does |
17
+ |-----------|-----------------------------|--------------|
18
+ | gem → app | `ruby_cms update` | Pulls new gem versions of installed modules into the app. Changed-files-only. Honors `.ruby_cms.yml` excludes. |
19
+ | app → gem | `ruby_cms sync` | Pushes app edits of gem-owned files back into gem templates. Only updates files the gem already ships unless `--include-new`. |
20
+
21
+ Always `--dry-run` first, review diffs, then run for real.
22
+
23
+ ## What is gem-owned vs app-specific
24
+
25
+ - **Gem-owned** = listed in a module's `files`/`migrations` in
26
+ `lib/ruby_cms/manifest_data.rb`. These must round-trip through the gem.
27
+ - **App-specific** = not in the manifest, or matched by `.ruby_cms.yml`
28
+ `exclude:`. Never push these into the gem (`sync` skips new files by default
29
+ for exactly this reason).
30
+ - Never-touch (gem default excludes): `config/initializers/admin.rb`,
31
+ `admin_dashboard.rb`, `admin_nav.rb`, `admin_pages.rb`. `admin_nav.rb` is
32
+ regenerated on every update — custom sidebar entries go in `admin_pages.rb`.
33
+
34
+ ## Workflow: changing a gem-owned file (e.g. a bug fix)
35
+
36
+ 1. **Edit the gem template** under `lib/generators/ruby_cms/templates/` (source
37
+ of truth).
38
+ 2. **Propagate to both apps**: either run `ruby_cms update` in each app, or
39
+ mirror the identical edit by hand when the gem isn't wired into that app's
40
+ Gemfile yet.
41
+ 3. **Verify in each app** (boot / run the affected path).
42
+ 4. **Commit in all three repos** with matching intent.
43
+
44
+ ## Workflow: adding a new file or migration to the gem
45
+
46
+ 1. Add the template file under `templates/` (and `db/migrate/<ts>_*.rb` for
47
+ migrations, with `return if column_exists?`-style guards).
48
+ 2. Register it in the owning module's `files` / `migrations` array in
49
+ `manifest_data.rb`.
50
+ 3. In each app: `ruby_cms update` pulls the file; migrations are copied by
51
+ basename (gem timestamp ignored) and skipped if already present — then run
52
+ `bin/rails db:migrate`.
53
+ 4. Migrations get their **own app-local timestamp** per app. Keep the class name
54
+ and body identical to the gem template; only the timestamp differs.
55
+
56
+ ## Lockfile (`.ruby_cms.yml`)
57
+
58
+ - Records installed modules + `exclude:` globs. **Required** for `update`/`sync`
59
+ to know which modules to touch.
60
+ - Both `portfolio` and `gvexcelsior` have one. Adjust `exclude:` per app when
61
+ host files diverge from gem templates (brand CSS, Gmail delivery, merged locales).
62
+ - If gem i18n is merged into `config/locales/en.yml` / `nl.yml`, add those paths
63
+ to `exclude:` — `ruby_cms update` overwrites them wholesale. Pull new keys by
64
+ hand or keep app-only keys in separate `*.en.yml` files.
65
+
66
+ ## Gemfile
67
+
68
+ Keep the gem in the Gemfile only while installing/updating:
69
+
70
+ ```ruby
71
+ gem "ruby_cms", path: "../gems/ruby_cms"
72
+ ```
73
+
74
+ - `gvexcelsior`: currently active.
75
+ - `portfolio`: commented out — re-enable temporarily for `update`/`sync`, then
76
+ comment back.
77
+
78
+ ## Checklist for any gem-owned change
79
+
80
+ - [ ] Gem template edited (source of truth)
81
+ - [ ] `manifest_data.rb` updated if a file/migration was added
82
+ - [ ] gvexcelsior updated (+ `db:migrate` if migration) and verified
83
+ - [ ] portfolio updated (+ `db:migrate` if migration) and verified
84
+ - [ ] Diffs reviewed and committed in gem, gvexcelsior, and portfolio
data/exe/ruby_cms CHANGED
@@ -1,25 +1,20 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Find Rails app root (current dir or nearest parent with config/application.rb)
5
- app_root = File.expand_path(Dir.pwd)
6
- loop do
7
- break if File.file?(File.join(app_root, "config/application.rb"))
8
-
9
- parent = File.dirname(app_root)
10
- break if parent == app_root
11
-
12
- app_root = parent
13
- end
14
-
15
- unless File.file?(File.join(app_root, "config/application.rb"))
16
- warn "Not in a Rails application (config/application.rb not found)."
17
- warn "Run from your app root, or use: rails ruby_cms:setup_admin"
4
+ # The CLI only needs the gem's own code (it shells out to bin/rails); it never boots
5
+ # the host Rails app. If loading fails — usually because a stale globally-installed
6
+ # ruby_cms is picked up instead of the one in your Gemfile — guide the user instead of
7
+ # dumping a stacktrace.
8
+ begin
9
+ require "ruby_cms/cli"
10
+ rescue LoadError => e
11
+ warn "ruby_cms: could not load the CLI (#{e.class}: #{e.message})."
12
+ warn ""
13
+ warn "Run it through Bundler from your app so the right version is used:"
14
+ warn " bundle exec ruby_cms #{ARGV.join(' ')}".rstrip
15
+ warn ""
16
+ warn "(A stale globally-installed ruby_cms can shadow the one in your Gemfile.)"
18
17
  exit 1
19
18
  end
20
19
 
21
- Dir.chdir(app_root)
22
- require File.join(app_root, "config/environment")
23
-
24
- require "ruby_cms/cli"
25
20
  RubyCms::CLI.start(ARGV)
@@ -2,121 +2,177 @@
2
2
 
3
3
  module RubyCms
4
4
  module Generators
5
+ # Scaffolds a new admin page that matches the modern RubyCMS conventions:
6
+ # an Admin::ApplicationController subclass using the shared concern stack
7
+ # (pagination, turbo tables, bulk actions, audit), a data_table-backed index,
8
+ # and — unless --read-only — full CRUD with a form.
9
+ #
10
+ # rails g ruby_cms:admin_page product name:string price:decimal published:boolean
11
+ # rails g ruby_cms:admin_page report --read-only --section settings --icon chart_bar
12
+ #
13
+ # Fields are optional "name[:type]" pairs (type defaults to string). They drive
14
+ # the strong params, the form inputs, the table headers and the row cells.
5
15
  class AdminPageGenerator < ::Rails::Generators::NamedBase
6
16
  source_root File.expand_path("templates/admin_page", __dir__)
7
17
 
18
+ argument :attributes, type: :array, default: [],
19
+ banner: "field[:type] field[:type]"
20
+
8
21
  class_option :permission, type: :string, default: nil,
9
- desc: "Permission key (default: manage_<name>)"
22
+ desc: "Permission key (default: manage_<plural_name>)"
10
23
  class_option :icon, type: :string, default: "folder",
11
- desc: "Named icon from RubyCms::Icons (e.g. archive_box, bell, clock). " \
24
+ desc: "Named icon from RubyCms::Icons (e.g. archive_box, bell, chart_bar). " \
12
25
  "Run RubyCms::Icons.available for the full list."
13
26
  class_option :section, type: :string, default: "main",
14
27
  desc: "Navigation section: main or settings"
15
28
  class_option :order, type: :numeric, default: 10,
16
29
  desc: "Sort order within the nav section"
30
+ class_option :read_only, type: :boolean, default: false,
31
+ desc: "Generate an index-only page (no form / create / update / destroy)"
32
+ class_option :skip_route, type: :boolean, default: false,
33
+ desc: "Don't touch config/routes.rb"
34
+ class_option :skip_nav, type: :boolean, default: false,
35
+ desc: "Don't register the page in the sidebar"
17
36
 
18
37
  def validate_options
19
- raise ArgumentError, "--section must be 'main' or 'settings', got '#{section_name}'" unless %w[main settings].include?(section_name)
38
+ raise ArgumentError, "--section must be 'main' or 'settings', got '#{section_name}'" \
39
+ unless %w[main settings].include?(section_name)
20
40
 
21
- icon_sym = icon_name.to_sym
22
- return if RubyCms::Icons::REGISTRY.key?(icon_sym)
41
+ return unless defined?(RubyCms::Icons::REGISTRY)
42
+ return if RubyCms::Icons::REGISTRY.key?(icon_name.to_sym)
23
43
 
24
- say "Warning: icon '#{icon_name}' is not in RubyCms::Icons::REGISTRY. " \
25
- "Available: #{RubyCms::Icons.available.join(', ')}", :yellow
44
+ say " Icon '#{icon_name}' not found in RubyCms::Icons. " \
45
+ "Available: #{RubyCms::Icons.available.join(', ')}. " \
46
+ "Continuing — pass any registered name with --icon.", :yellow
26
47
  end
27
48
 
28
49
  def create_controller
29
50
  template "controller.rb.tt",
30
- File.join("app/controllers/admin", "#{file_name}_controller.rb")
51
+ File.join("app/controllers/admin", "#{file_name.pluralize}_controller.rb")
31
52
  end
32
53
 
33
- def create_view
54
+ def create_index
34
55
  template "index.html.erb.tt",
35
- File.join("app/views/admin", file_name, "index.html.erb")
56
+ File.join(view_dir, "index.html.erb")
57
+ template "_admin_table_content.html.erb.tt",
58
+ File.join(view_dir, "_admin_table_content.html.erb")
59
+ template "_row.html.erb.tt",
60
+ File.join(view_dir, "_row.html.erb")
61
+ end
62
+
63
+ def create_form
64
+ return if read_only?
65
+
66
+ template "_form.html.erb.tt", File.join(view_dir, "_form.html.erb")
67
+ template "new.html.erb.tt", File.join(view_dir, "new.html.erb")
68
+ template "edit.html.erb.tt", File.join(view_dir, "edit.html.erb")
36
69
  end
37
70
 
38
71
  def add_route
72
+ return if options[:skip_route]
73
+
39
74
  routes_path = Rails.root.join("config/routes.rb")
40
- return unless routes_path.exist?
75
+ return say("⚠ config/routes.rb not found, skipping route.", :yellow) unless routes_path.exist?
41
76
 
42
77
  content = File.read(routes_path)
43
- route_line = " resources :#{plural_name}, only: [:index], controller: \"admin/#{file_name}\""
44
-
45
- if content.include?("resources :#{plural_name}") && content.include?("admin/#{file_name}")
46
- say "Route already exists, skipping.", :yellow
47
- return
48
- end
78
+ return say("⚠ Route resources :#{plural_name} already present, skipping.", :yellow) \
79
+ if content.match?(/resources\s+:#{plural_name}\b/)
49
80
 
50
81
  if content.match?(/namespace\s+:admin\s+do/)
51
- inject_into_file routes_path.to_s, after: /namespace\s+:admin\s+do\s*\n/ do
52
- "#{route_line}\n"
53
- end
82
+ inject_into_file routes_path.to_s, route_block, after: /namespace\s+:admin\s+do\s*\n/
54
83
  else
55
- route <<~RUBY
56
- namespace :admin do
57
- #{route_line}
58
- end
59
- RUBY
84
+ route "namespace :admin do\n#{route_block}end\n"
60
85
  end
61
- say "Route: added admin/#{plural_name}", :green
86
+ say "Route: added resources :#{plural_name} to the admin namespace.", :green
62
87
  end
63
88
 
64
- def add_page_registration
65
- pages_file = Rails.root.join("config/initializers/ruby_cms_pages.rb")
89
+ def register_nav
90
+ return if options[:skip_nav]
66
91
 
67
- registration = register_page_code
92
+ # Write to a host-owned initializer (NOT admin_nav.rb — that file is
93
+ # regenerated by `ruby_cms update` and would clobber app entries).
94
+ target = Rails.root.join("config/initializers/admin_pages.rb")
68
95
 
69
- if pages_file.exist?
70
- content = File.read(pages_file)
71
- if content.include?("key: :#{file_name}")
72
- say "Page registration already exists, skipping.", :yellow
73
- return
74
- end
75
- append_to_file pages_file.to_s, "\n#{registration}\n"
76
- else
77
- create_file pages_file.to_s, "# frozen_string_literal: true\n\n#{registration}\n"
78
- end
79
- say "Registered page :#{file_name} in config/initializers/ruby_cms_pages.rb", :green
96
+ return say("⚠ Page :#{plural_name} already registered in admin_pages.rb, skipping.", :yellow) \
97
+ if target.exist? && File.read(target).include?("key: :#{plural_name}")
98
+
99
+ create_file target.to_s, "# frozen_string_literal: true\n" unless target.exist?
100
+ append_to_file target.to_s, "\n#{register_page_block}\n"
101
+ say "✓ Registered :#{plural_name} in config/initializers/admin_pages.rb.", :green
80
102
  end
81
103
 
82
104
  def show_next_steps
83
- say "\nAdmin page '#{file_name}' created.", :green
84
- say "Next: rails ruby_cms:seed_permissions", :cyan
105
+ say "\n✓ Admin page '#{plural_name}' created.", :green
106
+ say " Controller: app/controllers/admin/#{plural_name}_controller.rb"
107
+ say " Views: #{view_dir}/"
108
+ say ""
109
+ say "Next steps:", :cyan
110
+ say " rails ruby_cms:seed_permissions # registers :#{permission_key}", :cyan
111
+ say " # Adjust the generated _row / _form to match your columns" unless fields.any?
85
112
  end
86
113
 
87
- private
114
+ # Value object for a scaffolded column.
115
+ Field = Struct.new(:name, :type) do
116
+ def label = name.to_s.humanize
117
+
118
+ def form_input(form_var)
119
+ case type
120
+ when :text then "#{form_var}.text_area :#{name}, rows: 4"
121
+ when :integer then "#{form_var}.number_field :#{name}"
122
+ when :decimal, :float then "#{form_var}.number_field :#{name}, step: \"any\""
123
+ when :boolean then "#{form_var}.check_box :#{name}"
124
+ when :date then "#{form_var}.date_field :#{name}"
125
+ when :datetime, :timestamp then "#{form_var}.datetime_field :#{name}"
126
+ when :time then "#{form_var}.time_field :#{name}"
127
+ else "#{form_var}.text_field :#{name}"
128
+ end
129
+ end
88
130
 
89
- def permission_key
90
- options[:permission].presence || "manage_#{file_name}"
131
+ def boolean? = type == :boolean
91
132
  end
92
133
 
93
- def icon_name
94
- options[:icon]
95
- end
134
+ private
96
135
 
97
- def section_name
98
- options[:section]
99
- end
136
+ def read_only? = options[:read_only]
137
+
138
+ def view_dir = File.join("app/views/admin", plural_name)
100
139
 
101
- def order_value
102
- options[:order]
140
+ def permission_key = options[:permission].presence || "manage_#{plural_name}"
141
+
142
+ def icon_name = options[:icon]
143
+
144
+ def section_name = options[:section]
145
+
146
+ # Parsed "name[:type]" pairs. Unknown/blank types fall back to :string.
147
+ def fields
148
+ @fields ||= attributes.filter_map do |raw|
149
+ name, type = raw.to_s.split(":", 2)
150
+ next if name.blank?
151
+
152
+ Field.new(name, (type.presence || "string").to_sym)
153
+ end
103
154
  end
104
155
 
105
- def path_helper_name
106
- "admin_#{plural_name}_path"
156
+ def permit_list = fields.map {|f| ":#{f.name}" }.join(", ")
157
+
158
+ def route_block
159
+ only = read_only? ? ", only: %i[index]" : ""
160
+ block = " resources :#{plural_name}#{only} do\n"
161
+ block << " delete :bulk_delete, on: :collection\n" unless read_only?
162
+ block << " end\n"
107
163
  end
108
164
 
109
- def register_page_code
165
+ def register_page_block
110
166
  <<~RUBY
111
167
  Rails.application.config.to_prepare do
112
168
  RubyCms.register_page(
113
- key: :#{file_name},
114
- label: "#{human_name}",
115
- path: :#{path_helper_name},
169
+ key: :#{plural_name},
170
+ label: "#{human_name.pluralize}",
171
+ path: lambda(&:admin_#{plural_name}_path),
116
172
  icon: :#{icon_name},
117
173
  section: :#{section_name},
118
174
  permission: :#{permission_key},
119
- order: #{order_value}
175
+ order: #{options[:order]}
120
176
  )
121
177
  end
122
178
  RUBY
@@ -8,37 +8,54 @@
8
8
  <%= csrf_meta_tags %>
9
9
  <%= csp_meta_tag %>
10
10
 
11
- <%= favicon_link_tag asset_path("favicon.ico") rescue nil %>
12
- <link rel="icon" type="image/png" sizes="32x32" href="<%= asset_path("favicon-32x32.png") %>">
13
- <link rel="icon" type="image/png" sizes="16x16" href="<%= asset_path("favicon-16x16.png") %>">
14
- <link rel="apple-touch-icon" href="<%= asset_path("apple-touch-icon.png") %>">
15
- <link rel="manifest" href="<%= asset_path("site.webmanifest") %>">
11
+ <%= favicon_link_tag(asset_path("favicon.ico")) rescue nil %>
12
+ <% %w[favicon-32x32.png favicon-16x16.png apple-touch-icon.png site.webmanifest].each do |asset| %>
13
+ <% path = (asset_path(asset) rescue nil) %>
14
+ <% next unless path %>
15
+ <% if asset == "site.webmanifest" %>
16
+ <link rel="manifest" href="<%= path %>">
17
+ <% elsif asset == "apple-touch-icon.png" %>
18
+ <link rel="apple-touch-icon" href="<%= path %>">
19
+ <% else %>
20
+ <link rel="icon" type="image/png" sizes="<%= asset.include?("32") ? "32x32" : "16x16" %>" href="<%= path %>">
21
+ <% end %>
22
+ <% end %>
16
23
 
17
24
  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
18
- <%= stylesheet_link_tag "ruby_cms/admin", "data-turbo-track": "reload" rescue nil %>
25
+ <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
19
26
  <%= stylesheet_link_tag "actiontext", "data-turbo-track": "reload" rescue nil %>
20
27
  <%= javascript_importmap_tags "admin" %>
21
28
  </head>
22
- <body class="min-h-screen bg-gray-50 text-gray-900">
23
- <div class="flex h-screen overflow-hidden" data-controller="ruby-cms--mobile-menu">
24
- <%# Mobile menu toggle (visible on md and below) %>
25
- <button type="button" class="md:hidden fixed top-4 left-4 z-50 inline-flex items-center justify-center p-2 bg-white border border-gray-200/80 rounded-lg shadow-sm cursor-pointer" aria-label="Toggle menu" data-action="click->ruby-cms--mobile-menu#toggle">
29
+ <body class="min-h-screen bg-[#FAF9F5] text-gray-900"
30
+ data-controller="admin--shortcuts-overlay admin--command-palette admin--nav-shortcuts"
31
+ data-action="keydown@window->admin--shortcuts-overlay#onWindowKey keydown@window->admin--command-palette#onWindowKey keydown@window->admin--nav-shortcuts#onKey"
32
+ data-admin--nav-shortcuts-map-value='<%= {
33
+ "d" => admin_root_path,
34
+ "v" => admin_visual_editor_path,
35
+ "c" => admin_content_blocks_path,
36
+ "a" => admin_analytics_path,
37
+ "s" => admin_settings_path,
38
+ "u" => admin_users_path
39
+ }.compact.to_json %>'>
40
+ <div class="flex h-screen overflow-hidden" data-controller="admin--mobile-menu">
41
+ <%# Mobile menu toggle (below lg, same breakpoint as sidebar drawer) %>
42
+ <button type="button" class="lg:hidden fixed left-4 top-[max(1rem,env(safe-area-inset-top))] z-50 inline-flex items-center justify-center rounded-lg border border-gray-200/80 bg-white p-2 shadow-sm cursor-pointer" aria-label="Toggle menu" data-action="click->admin--mobile-menu#toggle">
26
43
  <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
27
44
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
28
45
  </svg>
29
46
  </button>
30
47
 
31
- <%= render "layouts/ruby_cms/admin_sidebar" %>
48
+ <%= render "layouts/admin/admin_sidebar" %>
32
49
 
33
50
  <%# Mobile overlay (JS adds class "show") %>
34
- <div class="hidden fixed inset-0 bg-black/50 z-40 lg:hidden" data-ruby-cms--mobile-menu-target="overlay" data-action="click->ruby-cms--mobile-menu#close"></div>
51
+ <div class="hidden fixed inset-0 bg-black/50 z-40 lg:hidden" data-admin--mobile-menu-target="overlay" data-action="click->admin--mobile-menu#close"></div>
35
52
 
36
- <main class="flex-1 flex flex-col overflow-hidden bg-gray-50 min-w-0" role="main">
53
+ <main class="flex-1 flex flex-col overflow-hidden bg-[#FAF9F5] min-w-0" role="main">
37
54
  <% if content_for?(:full_width) %>
38
55
  <%= yield %>
39
56
  <% else %>
40
- <div class="flex-1 flex flex-col min-h-0 p-6 overflow-y-auto bg-gray-50">
41
- <div class="mx-auto w-full max-w-7xl">
57
+ <div class="flex-1 flex flex-col min-h-0 overflow-y-auto bg-[#FAF9F5] px-3 pb-4 pt-[calc(env(safe-area-inset-top)+3.5rem)] sm:px-6 sm:pb-6 lg:pt-6">
58
+ <div class="w-full min-w-0">
42
59
  <% if content_for?(:title) || content_for?(:header_actions) %>
43
60
  <header class="flex-shrink-0 mb-4 <%= yield(:header_styles) if content_for?(:header_styles) %>">
44
61
  <div class="flex flex-wrap items-center justify-between gap-4">
@@ -69,7 +86,7 @@
69
86
  </nav>
70
87
  <% end %>
71
88
 
72
- <%= render "layouts/ruby_cms/admin_flash_messages" %>
89
+ <%= render "layouts/admin/admin_flash_messages" %>
73
90
 
74
91
  <%= yield %>
75
92
  </div>
@@ -77,6 +94,7 @@
77
94
  <% end %>
78
95
  </main>
79
96
  </div>
97
+ <%= render "layouts/admin/command_palette" %>
98
+ <%= render "layouts/admin/shortcuts_overlay" %>
80
99
  </body>
81
100
  </html>
82
-
@@ -0,0 +1,25 @@
1
+ <%%= render partial: "admin/shared/data_table", locals: {
2
+ id: "admin_table_content",
3
+ headers: [
4
+ <%- if fields.any? -%>
5
+ <%- fields.each do |f| -%>
6
+ { key: :<%= f.name %>, label: "<%= f.label %>", sortable: true },
7
+ <%- end -%>
8
+ <%- else -%>
9
+ { key: :record, label: "<%= human_name %>", sortable: false },
10
+ <%- end -%>
11
+ { key: :actions, label: "", actions: true, width: "40px", align: :right }
12
+ ],
13
+ collection: @<%= plural_name %> || [],
14
+ row_partial: "admin/<%= plural_name %>/row",
15
+ <%- unless read_only? -%>
16
+ bulk_delete_url: bulk_delete_admin_<%= plural_name %>_path,
17
+ row_click_url: ->(item) { edit_admin_<%= singular_name %>_path(item) },
18
+ <%- end -%>
19
+ empty_state: {
20
+ title: "Nog geen <%= human_name.downcase.pluralize %>",
21
+ description: "<%= read_only? ? "Er zijn nog geen #{human_name.downcase.pluralize}." : "Maak je eerste #{human_name.downcase} aan met de knop rechtsboven." %>"
22
+ },
23
+ pagination: @pagination,
24
+ pagination_path: admin_<%= plural_name %>_path
25
+ } %>
@@ -0,0 +1,36 @@
1
+ <%%= form_with model: [:admin, @<%= singular_name %>], class: "max-w-2xl space-y-5" do |f| %>
2
+ <%% if @<%= singular_name %>.errors.any? %>
3
+ <div class="rounded-md border border-red-200 bg-red-50 px-4 py-3 text-[13px] text-red-700">
4
+ <p class="font-semibold mb-1"><%%= pluralize(@<%= singular_name %>.errors.count, "fout") %> voorkwamen het opslaan:</p>
5
+ <ul class="list-disc list-inside space-y-0.5">
6
+ <%% @<%= singular_name %>.errors.full_messages.each do |msg| %>
7
+ <li><%%= msg %></li>
8
+ <%% end %>
9
+ </ul>
10
+ </div>
11
+ <%% end %>
12
+
13
+ <%- if fields.any? -%>
14
+ <%- fields.each do |field| -%>
15
+ <%- if field.boolean? -%>
16
+ <label class="flex items-center gap-2 text-[13px] font-medium text-[#1c1917]">
17
+ <%%= <%= field.form_input("f") %> %>
18
+ <%= field.label %>
19
+ </label>
20
+ <%- else -%>
21
+ <div class="space-y-1.5">
22
+ <%%= f.label :<%= field.name %>, "<%= field.label %>", class: "block text-[13px] font-medium text-[#1c1917]" %>
23
+ <%%= <%= field.form_input("f") %>, class: "block w-full rounded-md border border-[#e9e4d4] px-3 py-2 text-[13px] focus:border-blue-500 focus:ring-1 focus:ring-blue-500" %>
24
+ </div>
25
+ <%- end -%>
26
+ <%- end -%>
27
+ <%- else -%>
28
+ <%%# TODO: add form fields for <%= class_name %> (re-run with field:type args to scaffold them) %>
29
+ <%- end -%>
30
+
31
+ <div class="flex items-center gap-2 pt-2">
32
+ <%%= f.submit class: "inline-flex items-center h-9 px-4 rounded-md bg-blue-600 text-white text-[13px] font-medium hover:bg-blue-700 transition-colors cursor-pointer" %>
33
+ <%%= link_to "Annuleren", admin_<%= plural_name %>_path,
34
+ class: "inline-flex items-center h-9 px-4 rounded-md border border-[#e9e4d4] bg-white text-[#1c1917] text-[13px] font-medium hover:bg-[#faf8f3] transition-colors no-underline" %>
35
+ </div>
36
+ <%% end %>
@@ -0,0 +1,20 @@
1
+ <%% item = local_assigns[:<%= singular_name %>] || local_assigns[:item] %>
2
+ <%- if fields.any? -%>
3
+ <%- fields.each do |f| -%>
4
+ <td data-column="<%= f.name %>" class="py-1.5 px-3 align-middle text-[13px] text-[#1c1917]">
5
+ <%- if f.boolean? -%>
6
+ <%% if item.<%= f.name %> %>
7
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium bg-green-50 text-green-700 border border-green-100">Ja</span>
8
+ <%% else %>
9
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium bg-[#faf8f3] text-[#78716c] border border-[#e9e4d4]">Nee</span>
10
+ <%% end %>
11
+ <%- else -%>
12
+ <%%= item.<%= f.name %> %>
13
+ <%- end -%>
14
+ </td>
15
+ <%- end -%>
16
+ <%- else -%>
17
+ <td data-column="record" class="py-1.5 px-3 align-middle text-[13px] text-[#1c1917]">
18
+ <%%= item.to_s %>
19
+ </td>
20
+ <%- end -%>
@@ -1,8 +1,80 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Admin::<%= class_name %>Controller < RubyCms::Admin::BaseController
4
- cms_page :<%= file_name %>
3
+ class Admin::<%= plural_name.camelize %>Controller < Admin::ApplicationController
4
+ include AdminPagination
5
+ include AdminTurboTable
6
+ include AdminBulkActions
7
+ include AuditLoggable
8
+
9
+ paginates_with_pref :<%= plural_name %>, default: 25
10
+
11
+ before_action { require_permission!(:<%= permission_key %>) }
12
+ <%- unless read_only? -%>
13
+ before_action :set_<%= singular_name %>, only: %i[show edit update destroy]
14
+ <%- end -%>
5
15
 
6
16
  def index
17
+ scope = <%= class_name %>.all.order(created_at: :desc)
18
+ @<%= plural_name %> = set_pagination_vars(scope, per_page: 25)
19
+ turbo_render_index
20
+ end
21
+ <%- unless read_only? -%>
22
+
23
+ def show
24
+ redirect_to edit_admin_<%= singular_name %>_path(@<%= singular_name %>)
25
+ end
26
+
27
+ def new
28
+ @<%= singular_name %> = <%= class_name %>.new
29
+ end
30
+
31
+ def create
32
+ @<%= singular_name %> = <%= class_name %>.new(<%= singular_name %>_params)
33
+ if @<%= singular_name %>.save
34
+ audit!(:<%= singular_name %>_created, target: @<%= singular_name %>, summary: "Created <%= class_name %>##{@<%= singular_name %>.id}")
35
+ redirect_to admin_<%= plural_name %>_path, notice: "<%= human_name %> aangemaakt."
36
+ else
37
+ render :new, status: :unprocessable_entity
38
+ end
39
+ end
40
+
41
+ def edit; end
42
+
43
+ def update
44
+ if @<%= singular_name %>.update(<%= singular_name %>_params)
45
+ audit!(:<%= singular_name %>_updated, target: @<%= singular_name %>, summary: "Updated <%= class_name %>##{@<%= singular_name %>.id}")
46
+ redirect_to admin_<%= plural_name %>_path, notice: "<%= human_name %> bijgewerkt."
47
+ else
48
+ render :edit, status: :unprocessable_entity
49
+ end
50
+ end
51
+
52
+ def destroy
53
+ id = @<%= singular_name %>.id
54
+ @<%= singular_name %>.destroy
55
+ audit!(:<%= singular_name %>_deleted, target: "<%= class_name %>##{id}", summary: "Deleted <%= class_name %>##{id}", meta: { id: id })
56
+ redirect_to admin_<%= plural_name %>_path, notice: "<%= human_name %> verwijderd."
57
+ end
58
+
59
+ def bulk_delete
60
+ count = bulk_destroy(<%= class_name %>, audit: :<%= singular_name %>_deleted, target_label: "<%= class_name %>")
61
+ redirect_to admin_<%= plural_name %>_path, notice: "#{count} verwijderd."
62
+ end
63
+ <%- end -%>
64
+
65
+ private
66
+ <%- unless read_only? -%>
67
+
68
+ def set_<%= singular_name %>
69
+ @<%= singular_name %> = <%= class_name %>.find(params[:id])
70
+ end
71
+
72
+ def <%= singular_name %>_params
73
+ <%- if fields.any? -%>
74
+ params.require(:<%= singular_name %>).permit(<%= permit_list %>)
75
+ <%- else -%>
76
+ params.require(:<%= singular_name %>).permit() # TODO: list permitted attributes
77
+ <%- end -%>
7
78
  end
79
+ <%- end -%>
8
80
  end