ruby_cms 0.1.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.
- checksums.yaml +7 -0
- data/.cursor/dhh.mdc +698 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/README.md +235 -0
- data/Rakefile +30 -0
- data/app/components/ruby_cms/admin/admin_page/admin_table_content.rb +32 -0
- data/app/components/ruby_cms/admin/admin_page.rb +345 -0
- data/app/components/ruby_cms/admin/base_component.rb +78 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table.rb +149 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_actions.rb +127 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_body.rb +15 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_cell.rb +41 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_head.rb +33 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_delete_modal.rb +174 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header.rb +59 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header_bar.rb +159 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_pagination.rb +192 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_row.rb +97 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_actions.rb +137 -0
- data/app/controllers/concerns/ruby_cms/admin_pagination.rb +120 -0
- data/app/controllers/concerns/ruby_cms/admin_turbo_table.rb +68 -0
- data/app/controllers/concerns/ruby_cms/page_tracking.rb +52 -0
- data/app/controllers/concerns/ruby_cms/visitor_error_capture.rb +39 -0
- data/app/controllers/ruby_cms/admin/analytics_controller.rb +191 -0
- data/app/controllers/ruby_cms/admin/base_controller.rb +105 -0
- data/app/controllers/ruby_cms/admin/content_blocks_controller.rb +390 -0
- data/app/controllers/ruby_cms/admin/dashboard_controller.rb +50 -0
- data/app/controllers/ruby_cms/admin/locale_controller.rb +20 -0
- data/app/controllers/ruby_cms/admin/permissions_controller.rb +66 -0
- data/app/controllers/ruby_cms/admin/settings_controller.rb +223 -0
- data/app/controllers/ruby_cms/admin/user_permissions_controller.rb +59 -0
- data/app/controllers/ruby_cms/admin/users_controller.rb +107 -0
- data/app/controllers/ruby_cms/admin/visitor_errors_controller.rb +89 -0
- data/app/controllers/ruby_cms/admin/visual_editor_controller.rb +322 -0
- data/app/controllers/ruby_cms/errors_controller.rb +35 -0
- data/app/helpers/ruby_cms/admin/admin_page_helper.rb +21 -0
- data/app/helpers/ruby_cms/admin/bulk_action_table_helper.rb +159 -0
- data/app/helpers/ruby_cms/application_helper.rb +41 -0
- data/app/helpers/ruby_cms/bulk_action_table_helper.rb +151 -0
- data/app/helpers/ruby_cms/content_blocks_helper.rb +375 -0
- data/app/helpers/ruby_cms/settings_helper.rb +160 -0
- data/app/javascript/controllers/ruby_cms/auto_save_preference_controller.js +73 -0
- data/app/javascript/controllers/ruby_cms/bulk_action_table_controller.js +553 -0
- data/app/javascript/controllers/ruby_cms/clickable_row_controller.js +28 -0
- data/app/javascript/controllers/ruby_cms/flash_messages_controller.js +29 -0
- data/app/javascript/controllers/ruby_cms/index.js +104 -0
- data/app/javascript/controllers/ruby_cms/locale_tabs_controller.js +34 -0
- data/app/javascript/controllers/ruby_cms/mobile_menu_controller.js +55 -0
- data/app/javascript/controllers/ruby_cms/nav_order_sortable_controller.js +192 -0
- data/app/javascript/controllers/ruby_cms/page_preview_controller.js +135 -0
- data/app/javascript/controllers/ruby_cms/toggle_controller.js +39 -0
- data/app/javascript/controllers/ruby_cms/visual_editor_controller.js +321 -0
- data/app/models/concerns/content_block/publishable.rb +54 -0
- data/app/models/concerns/content_block/searchable.rb +22 -0
- data/app/models/content_block.rb +155 -0
- data/app/models/ruby_cms/content_block.rb +8 -0
- data/app/models/ruby_cms/permission.rb +28 -0
- data/app/models/ruby_cms/permittable.rb +39 -0
- data/app/models/ruby_cms/preference.rb +111 -0
- data/app/models/ruby_cms/user_permission.rb +12 -0
- data/app/models/ruby_cms/visitor_error.rb +109 -0
- data/app/services/ruby_cms/analytics/report.rb +362 -0
- data/app/services/ruby_cms/security_tracker.rb +92 -0
- data/app/views/layouts/ruby_cms/_admin_flash_messages.html.erb +37 -0
- data/app/views/layouts/ruby_cms/_admin_sidebar.html.erb +121 -0
- data/app/views/layouts/ruby_cms/admin.html.erb +81 -0
- data/app/views/layouts/ruby_cms/minimal.html.erb +181 -0
- data/app/views/ruby_cms/admin/analytics/index.html.erb +160 -0
- data/app/views/ruby_cms/admin/analytics/page_details.html.erb +84 -0
- data/app/views/ruby_cms/admin/analytics/partials/_back_button.html.erb +3 -0
- data/app/views/ruby_cms/admin/analytics/partials/_browser_device.html.erb +40 -0
- data/app/views/ruby_cms/admin/analytics/partials/_daily_activity_chart.html.erb +58 -0
- data/app/views/ruby_cms/admin/analytics/partials/_hourly_activity_chart.html.erb +51 -0
- data/app/views/ruby_cms/admin/analytics/partials/_recent_activity.html.erb +31 -0
- data/app/views/ruby_cms/admin/analytics/partials/_security_alert.html.erb +4 -0
- data/app/views/ruby_cms/admin/analytics/partials/_top_referrers.html.erb +21 -0
- data/app/views/ruby_cms/admin/analytics/visitor_details.html.erb +125 -0
- data/app/views/ruby_cms/admin/content_blocks/_form.html.erb +161 -0
- data/app/views/ruby_cms/admin/content_blocks/_row.html.erb +25 -0
- data/app/views/ruby_cms/admin/content_blocks/edit.html.erb +17 -0
- data/app/views/ruby_cms/admin/content_blocks/index.html.erb +66 -0
- data/app/views/ruby_cms/admin/content_blocks/new.html.erb +5 -0
- data/app/views/ruby_cms/admin/content_blocks/show.html.erb +110 -0
- data/app/views/ruby_cms/admin/dashboard/index.html.erb +198 -0
- data/app/views/ruby_cms/admin/permissions/_row.html.erb +11 -0
- data/app/views/ruby_cms/admin/permissions/index.html.erb +62 -0
- data/app/views/ruby_cms/admin/settings/index.html.erb +220 -0
- data/app/views/ruby_cms/admin/shared/_bulk_action_table_index.html.erb +56 -0
- data/app/views/ruby_cms/admin/user_permissions/index.html.erb +55 -0
- data/app/views/ruby_cms/admin/users/_row.html.erb +14 -0
- data/app/views/ruby_cms/admin/users/index.html.erb +70 -0
- data/app/views/ruby_cms/admin/visitor_errors/_row.html.erb +35 -0
- data/app/views/ruby_cms/admin/visitor_errors/index.html.erb +57 -0
- data/app/views/ruby_cms/admin/visitor_errors/show.html.erb +147 -0
- data/app/views/ruby_cms/admin/visual_editor/index.html.erb +144 -0
- data/app/views/ruby_cms/errors/not_found.html.erb +92 -0
- data/config/database.yml +6 -0
- data/config/importmap.rb +36 -0
- data/config/locales/en.yml +101 -0
- data/config/routes.rb +65 -0
- data/db/migrate/20260125000001_create_ruby_cms_permissions.rb +14 -0
- data/db/migrate/20260125000002_create_ruby_cms_user_permissions.rb +14 -0
- data/db/migrate/20260125000003_create_ruby_cms_content_blocks.rb +19 -0
- data/db/migrate/20260125000010_add_indexes_to_ruby_cms_tables.rb +9 -0
- data/db/migrate/20260127000001_add_locale_to_ruby_cms_content_blocks.rb +34 -0
- data/db/migrate/20260129000001_create_ruby_cms_visitor_errors.rb +24 -0
- data/db/migrate/20260130000001_add_referer_and_query_to_ruby_cms_visitor_errors.rb +8 -0
- data/db/migrate/20260130000002_create_ruby_cms_preferences.rb +16 -0
- data/db/migrate/20260130000003_add_category_to_ruby_cms_preferences.rb +8 -0
- data/db/migrate/20260211000001_add_ruby_cms_analytics_fields_to_ahoy_events.rb +19 -0
- data/db/migrate/20260212000001_use_unprefixed_cms_tables.rb +146 -0
- data/exe/ruby_cms +25 -0
- data/lib/generators/ruby_cms/install_generator.rb +1062 -0
- data/lib/generators/ruby_cms/templates/admin.html.erb +82 -0
- data/lib/generators/ruby_cms/templates/ruby_cms.rb +86 -0
- data/lib/ruby_cms/app_integration.rb +82 -0
- data/lib/ruby_cms/cli.rb +169 -0
- data/lib/ruby_cms/content_blocks_grouping.rb +41 -0
- data/lib/ruby_cms/content_blocks_sync.rb +329 -0
- data/lib/ruby_cms/css_compiler.rb +35 -0
- data/lib/ruby_cms/engine.rb +498 -0
- data/lib/ruby_cms/settings.rb +145 -0
- data/lib/ruby_cms/settings_registry.rb +289 -0
- data/lib/ruby_cms/version.rb +5 -0
- data/lib/ruby_cms.rb +195 -0
- data/lib/tasks/ruby_cms.rake +27 -0
- data/log/test.log +17875 -0
- data/sig/ruby_cms.rbs +4 -0
- metadata +223 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<div class="rounded-lg border border-gray-200/80 bg-white shadow-sm overflow-hidden">
|
|
2
|
+
<div class="px-6 py-4 border-b border-gray-100">
|
|
3
|
+
<nav class="text-sm text-gray-500 mb-3">
|
|
4
|
+
<%= link_to t("ruby_cms.admin.navigation.admin", default: "Admin"), ruby_cms_admin_root_path, class: "hover:text-gray-700 transition-colors" %> /
|
|
5
|
+
<%= link_to t("ruby_cms.admin.navigation.visitor_errors", default: "Visitor Errors"), ruby_cms_admin_visitor_errors_path, class: "hover:text-gray-700 transition-colors" %> /
|
|
6
|
+
<span><%= t("ruby_cms.admin.visitor_errors.error", default: "Error") %> #<%= @visitor_error.id %></span>
|
|
7
|
+
</nav>
|
|
8
|
+
|
|
9
|
+
<div class="flex flex-wrap items-start justify-between gap-4">
|
|
10
|
+
<h1 class="text-lg font-semibold text-gray-900"><%= t("ruby_cms.admin.visitor_errors.error_details", default: "Error Details") %></h1>
|
|
11
|
+
<div class="flex items-center gap-2">
|
|
12
|
+
<% unless @visitor_error.resolved %>
|
|
13
|
+
<%= button_to resolve_ruby_cms_admin_visitor_error_path(@visitor_error),
|
|
14
|
+
method: :patch,
|
|
15
|
+
class: "inline-flex items-center gap-1.5 h-9 rounded-md bg-emerald-600 px-4 text-sm font-medium text-white shadow-sm hover:bg-emerald-700 transition-colors border-0 cursor-pointer",
|
|
16
|
+
data: { confirm: t("ruby_cms.admin.visitor_errors.mark_resolved_confirm", default: "Mark this error as resolved?") } do %>
|
|
17
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
18
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
19
|
+
</svg>
|
|
20
|
+
<%= t("ruby_cms.admin.visitor_errors.mark_as_resolved", default: "Mark as Resolved") %>
|
|
21
|
+
<% end %>
|
|
22
|
+
<% end %>
|
|
23
|
+
|
|
24
|
+
<%= link_to ruby_cms_admin_visitor_errors_path, class: "inline-flex items-center gap-1.5 h-9 rounded-md border border-gray-200 bg-white px-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 transition-colors no-underline" do %>
|
|
25
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
26
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
|
|
27
|
+
</svg>
|
|
28
|
+
<%= t("ruby_cms.admin.visitor_errors.back_to_errors", default: "Back to Errors") %>
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="flex items-center justify-between px-6 py-4 border-b border-gray-100">
|
|
35
|
+
<h3 class="text-base font-semibold text-gray-900"><%= t("ruby_cms.admin.visitor_errors.error_information", default: "Error Information") %></h3>
|
|
36
|
+
<% if @visitor_error.resolved %>
|
|
37
|
+
<span class="inline-flex items-center gap-1 rounded-full bg-emerald-50 px-3 py-1 text-sm font-medium text-emerald-700">
|
|
38
|
+
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
|
|
39
|
+
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
|
40
|
+
</svg>
|
|
41
|
+
<%= t("ruby_cms.admin.visitor_errors.resolved", default: "Resolved") %>
|
|
42
|
+
</span>
|
|
43
|
+
<% else %>
|
|
44
|
+
<span class="inline-flex items-center gap-1 rounded-full bg-rose-50 px-3 py-1 text-sm font-medium text-rose-700">
|
|
45
|
+
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
|
|
46
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
|
|
47
|
+
</svg>
|
|
48
|
+
<%= t("ruby_cms.admin.visitor_errors.open", default: "Open") %>
|
|
49
|
+
</span>
|
|
50
|
+
<% end %>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="p-6 space-y-6">
|
|
54
|
+
<div class="grid gap-6" style="grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));">
|
|
55
|
+
<div>
|
|
56
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.error_id", default: "Error ID") %></label>
|
|
57
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.id %></p>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div>
|
|
61
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.error_class", default: "Error Class") %></label>
|
|
62
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.error_class %></p>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div>
|
|
66
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.request_method", default: "Request Method") %></label>
|
|
67
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0"><%= @visitor_error.request_method %></p>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div>
|
|
71
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.request_path", default: "Request Path") %></label>
|
|
72
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.request_path %></p>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div>
|
|
76
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.ip_address", default: "IP Address") %></label>
|
|
77
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0"><%= @visitor_error.ip_address %></p>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div>
|
|
81
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.browser", default: "Browser") %></label>
|
|
82
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.browser_info %></p>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div>
|
|
86
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.occurred_at", default: "Occurred At") %></label>
|
|
87
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0"><%= @visitor_error.created_at.strftime("%B %d, %Y at %I:%M %p") %></p>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div>
|
|
91
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.last_updated", default: "Last Updated") %></label>
|
|
92
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0"><%= @visitor_error.updated_at.strftime("%B %d, %Y at %I:%M %p") %></p>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<% if @visitor_error.respond_to?(:referer) && @visitor_error.referer.present? %>
|
|
96
|
+
<div>
|
|
97
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.referer", default: "Referer") %></label>
|
|
98
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.referer %></p>
|
|
99
|
+
</div>
|
|
100
|
+
<% end %>
|
|
101
|
+
|
|
102
|
+
<% if @visitor_error.respond_to?(:query_string) && @visitor_error.query_string.present? %>
|
|
103
|
+
<div>
|
|
104
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.query_string", default: "Query String") %></label>
|
|
105
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.query_string %></p>
|
|
106
|
+
</div>
|
|
107
|
+
<% end %>
|
|
108
|
+
|
|
109
|
+
<% if @visitor_error.respond_to?(:session_id) && @visitor_error.session_id.present? %>
|
|
110
|
+
<div>
|
|
111
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.session_id", default: "Session ID") %></label>
|
|
112
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.session_id %></p>
|
|
113
|
+
</div>
|
|
114
|
+
<% end %>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div>
|
|
118
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.error_message", default: "Error Message") %></label>
|
|
119
|
+
<div class="rounded-md border border-rose-200 bg-rose-50 p-4">
|
|
120
|
+
<p class="text-sm font-mono text-rose-700 m-0"><%= @visitor_error.error_message %></p>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div>
|
|
125
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.codepath", default: "Code Path") %></label>
|
|
126
|
+
<div class="rounded-md border border-gray-200 bg-gray-50 p-4 overflow-x-auto">
|
|
127
|
+
<pre class="text-xs font-mono text-gray-900 m-0 whitespace-pre-wrap break-words"><%= @visitor_error.codepath %></pre>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<% if @visitor_error.request_params.present? %>
|
|
132
|
+
<div>
|
|
133
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.request_parameters", default: "Request Parameters") %></label>
|
|
134
|
+
<div class="rounded-md border border-gray-200 bg-gray-50 p-4 overflow-x-auto">
|
|
135
|
+
<pre class="text-xs font-mono text-gray-900 m-0 whitespace-pre-wrap break-words"><%= JSON.pretty_generate(JSON.parse(@visitor_error.request_params)) rescue @visitor_error.request_params %></pre>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
<% end %>
|
|
139
|
+
|
|
140
|
+
<% if @visitor_error.user_agent.present? %>
|
|
141
|
+
<div>
|
|
142
|
+
<label class="block text-xs font-medium uppercase tracking-wide text-gray-400 mb-1.5"><%= t("ruby_cms.admin.visitor_errors.user_agent", default: "User Agent") %></label>
|
|
143
|
+
<p class="text-sm font-mono text-gray-900 bg-gray-50 px-3 py-2 rounded-md m-0 break-all"><%= @visitor_error.user_agent %></p>
|
|
144
|
+
</div>
|
|
145
|
+
<% end %>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<% content_for :full_width, true %>
|
|
2
|
+
|
|
3
|
+
<div class="flex-1 flex flex-col min-h-0 p-6 overflow-y-auto">
|
|
4
|
+
<div class="mx-auto w-full max-w-7xl flex flex-col gap-4 flex-1 min-h-0">
|
|
5
|
+
<!-- Header -->
|
|
6
|
+
<div class="flex flex-wrap items-center justify-between gap-4">
|
|
7
|
+
<h1 class="text-lg font-semibold tracking-tight text-gray-900">Visual Editor</h1>
|
|
8
|
+
<%= form_with url: ruby_cms_admin_visual_editor_path, method: :get, class: "flex items-center gap-2", data: { turbo: false } do %>
|
|
9
|
+
<%= hidden_field_tag :edit_mode, @edit_mode %>
|
|
10
|
+
|
|
11
|
+
<% if @available_pages && @available_pages.size > 1 %>
|
|
12
|
+
<%= select_tag(
|
|
13
|
+
:page,
|
|
14
|
+
options_for_select(@available_pages.map { |k, v| [v[:name], k] }, @current_page),
|
|
15
|
+
class: "h-9 rounded-md border border-gray-200 bg-white px-3 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-teal-200",
|
|
16
|
+
onchange: "this.form.requestSubmit()"
|
|
17
|
+
) %>
|
|
18
|
+
<% else %>
|
|
19
|
+
<%= hidden_field_tag :page, @current_page %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Visual Editor -->
|
|
25
|
+
<div class="rounded-lg border border-gray-200/80 bg-white shadow-sm overflow-hidden flex flex-col flex-1 min-h-0"
|
|
26
|
+
data-controller="ruby-cms--visual-editor"
|
|
27
|
+
data-ruby-cms--visual-editor-current-page-value="<%= @current_page.to_s %>">
|
|
28
|
+
|
|
29
|
+
<!-- Preview iframe -->
|
|
30
|
+
<div class="flex-1 min-h-0 overflow-hidden">
|
|
31
|
+
<iframe
|
|
32
|
+
id="preview-frame"
|
|
33
|
+
class="w-full h-full border-0"
|
|
34
|
+
data-ruby-cms--visual-editor-target="previewFrame"
|
|
35
|
+
src="<%= ruby_cms_admin_visual_editor_page_preview_path(page: @current_page, edit_mode: @edit_mode) %>">
|
|
36
|
+
</iframe>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- Edit Modal (overlay) -->
|
|
40
|
+
<div class="hidden fixed inset-0 z-50 flex items-center justify-center p-4"
|
|
41
|
+
data-ruby-cms--visual-editor-target="modal">
|
|
42
|
+
<div class="absolute inset-0 bg-black/50" data-action="click->ruby-cms--visual-editor#closeModal"></div>
|
|
43
|
+
<div class="relative w-full max-w-xl bg-white rounded-lg shadow-xl flex flex-col max-h-[90vh]">
|
|
44
|
+
<!-- Modal header -->
|
|
45
|
+
<div class="flex items-start justify-between gap-4 px-6 py-4 border-b border-gray-200">
|
|
46
|
+
<div>
|
|
47
|
+
<h2 class="text-base font-semibold text-gray-900">Edit Content Block</h2>
|
|
48
|
+
<p class="text-sm text-gray-500 mt-0.5">
|
|
49
|
+
Block: <span class="inline-flex items-center rounded-md bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-200" data-ruby-cms--visual-editor-target="blockKey"></span>
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-500 hover:bg-gray-100 hover:text-gray-900 transition-colors"
|
|
55
|
+
data-action="click->ruby-cms--visual-editor#closeModal">
|
|
56
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
57
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
58
|
+
</svg>
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Modal body -->
|
|
63
|
+
<form class="flex flex-col flex-1 min-h-0 overflow-hidden"
|
|
64
|
+
data-action="submit->ruby-cms--visual-editor#saveContent keydown->ruby-cms--visual-editor#handleKeydown">
|
|
65
|
+
<div class="flex-1 overflow-y-auto px-6 py-4 space-y-4" data-ruby-cms--visual-editor-target="modalBody">
|
|
66
|
+
<div class="space-y-4">
|
|
67
|
+
<div>
|
|
68
|
+
<label class="block text-sm font-medium text-gray-700 mb-1" for="content-type">Content Type</label>
|
|
69
|
+
<select
|
|
70
|
+
id="content-type"
|
|
71
|
+
class="w-full h-9 rounded-md border border-gray-200 bg-white px-3 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-teal-200"
|
|
72
|
+
data-ruby-cms--visual-editor-target="contentType"
|
|
73
|
+
data-action="change->ruby-cms--visual-editor#changeContentType">
|
|
74
|
+
<option value="text">Text</option>
|
|
75
|
+
<option value="rich_text">Rich Text</option>
|
|
76
|
+
<option value="image">Image</option>
|
|
77
|
+
<option value="link">Link</option>
|
|
78
|
+
<option value="list">List</option>
|
|
79
|
+
</select>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div data-ruby-cms--visual-editor-target="textContainer">
|
|
83
|
+
<label class="block text-sm font-medium text-gray-700 mb-1" for="content">Content</label>
|
|
84
|
+
<textarea
|
|
85
|
+
id="content"
|
|
86
|
+
rows="10"
|
|
87
|
+
class="w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-teal-200 resize-y"
|
|
88
|
+
data-ruby-cms--visual-editor-target="contentInput"
|
|
89
|
+
data-action="input->ruby-cms--visual-editor#updateCharCount"></textarea>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<% if ::ContentBlock.respond_to?(:action_text_available?) && ::ContentBlock.action_text_available? %>
|
|
93
|
+
<div data-ruby-cms--visual-editor-target="richTextContainer">
|
|
94
|
+
<label class="block text-sm font-medium text-gray-700 mb-1" for="rich-content-input">Rich Content</label>
|
|
95
|
+
<input id="rich-content-input" type="hidden" name="rich_content" data-ruby-cms--visual-editor-target="richContentInput">
|
|
96
|
+
<trix-editor
|
|
97
|
+
input="rich-content-input"
|
|
98
|
+
class="w-full rounded-md border border-gray-200 bg-white text-sm shadow-sm focus:outline-none"
|
|
99
|
+
data-action="trix-change->ruby-cms--visual-editor#updateCharCount"></trix-editor>
|
|
100
|
+
</div>
|
|
101
|
+
<% else %>
|
|
102
|
+
<div data-ruby-cms--visual-editor-target="richTextContainer" style="display: none;">
|
|
103
|
+
<p class="text-sm text-gray-500">Rich text requires Action Text. Run: <code>bin/rails action_text:install</code></p>
|
|
104
|
+
</div>
|
|
105
|
+
<% end %>
|
|
106
|
+
|
|
107
|
+
<div class="flex items-center justify-between text-xs text-gray-400 pt-1">
|
|
108
|
+
<span data-ruby-cms--visual-editor-target="charCount">0 characters</span>
|
|
109
|
+
<span>Last updated: <span data-ruby-cms--visual-editor-target="lastUpdated">Never</span></span>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- Modal footer -->
|
|
115
|
+
<div class="flex items-center justify-end gap-2 px-6 py-4 border-t border-gray-200 bg-gray-50">
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
class="inline-flex h-9 items-center justify-center rounded-md border border-gray-200 bg-white px-4 text-sm font-medium text-gray-900 shadow-sm hover:bg-gray-50 transition-colors"
|
|
119
|
+
data-action="click->ruby-cms--visual-editor#closeModal">
|
|
120
|
+
Cancel
|
|
121
|
+
</button>
|
|
122
|
+
<button
|
|
123
|
+
type="submit"
|
|
124
|
+
class="inline-flex h-9 items-center justify-center rounded-md bg-teal-600 px-4 text-sm font-medium text-white shadow-sm hover:bg-teal-700 transition-colors"
|
|
125
|
+
data-ruby-cms--visual-editor-target="saveButton">
|
|
126
|
+
Save Changes
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
</form>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<!-- Success Toast -->
|
|
134
|
+
<div class="hidden fixed bottom-6 right-6 z-50" data-ruby-cms--visual-editor-target="toast">
|
|
135
|
+
<div class="flex items-center gap-2 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800 shadow-lg">
|
|
136
|
+
<svg class="h-5 w-5 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
137
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
138
|
+
</svg>
|
|
139
|
+
<span data-ruby-cms--visual-editor-target="toastMessage">Content updated successfully</span>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Page Not Found (404)</title>
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
background: #f5f5f5;
|
|
12
|
+
color: #333;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
}
|
|
18
|
+
.error-container {
|
|
19
|
+
text-align: center;
|
|
20
|
+
padding: 40px 20px;
|
|
21
|
+
max-width: 600px;
|
|
22
|
+
}
|
|
23
|
+
h1 {
|
|
24
|
+
font-size: 72px;
|
|
25
|
+
font-weight: 700;
|
|
26
|
+
margin: 0 0 20px;
|
|
27
|
+
color: #dc2626;
|
|
28
|
+
}
|
|
29
|
+
h2 {
|
|
30
|
+
font-size: 24px;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
margin: 0 0 16px;
|
|
33
|
+
color: #374151;
|
|
34
|
+
}
|
|
35
|
+
p {
|
|
36
|
+
font-size: 16px;
|
|
37
|
+
line-height: 1.6;
|
|
38
|
+
color: #6b7280;
|
|
39
|
+
margin: 0 0 32px;
|
|
40
|
+
}
|
|
41
|
+
a {
|
|
42
|
+
display: inline-block;
|
|
43
|
+
padding: 12px 24px;
|
|
44
|
+
background: #2563eb;
|
|
45
|
+
color: white;
|
|
46
|
+
text-decoration: none;
|
|
47
|
+
border-radius: 6px;
|
|
48
|
+
font-weight: 500;
|
|
49
|
+
transition: background 0.2s;
|
|
50
|
+
}
|
|
51
|
+
a:hover {
|
|
52
|
+
background: #1d4ed8;
|
|
53
|
+
}
|
|
54
|
+
.path {
|
|
55
|
+
font-family: "Monaco", "Courier New", monospace;
|
|
56
|
+
font-size: 14px;
|
|
57
|
+
color: #991b1b;
|
|
58
|
+
background: #fee2e2;
|
|
59
|
+
padding: 8px 12px;
|
|
60
|
+
border-radius: 4px;
|
|
61
|
+
margin: 24px 0;
|
|
62
|
+
word-break: break-all;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<div class="error-container">
|
|
68
|
+
<h1>404</h1>
|
|
69
|
+
<h2>Page Not Found</h2>
|
|
70
|
+
<p>The page you are looking for doesn't exist or has been moved.</p>
|
|
71
|
+
<div class="path"><%= ERB::Util.html_escape(request.path.to_s) %></div>
|
|
72
|
+
<a href="/">Go to Homepage</a>
|
|
73
|
+
</div>
|
|
74
|
+
</body>
|
|
75
|
+
</html>
|
|
76
|
+
<div class="rounded-lg border border-gray-200/80 bg-white shadow-sm p-8 max-w-lg mx-auto mt-12">
|
|
77
|
+
<nav class="text-sm text-gray-500 mb-4">
|
|
78
|
+
<%= link_to t("ruby_cms.admin.navigation.admin", default: "Admin"), ruby_cms_admin_root_path, class: "hover:text-gray-700 transition-colors" %> /
|
|
79
|
+
<span><%= t("ruby_cms.errors.not_found", default: "Page Not Found") %></span>
|
|
80
|
+
</nav>
|
|
81
|
+
|
|
82
|
+
<h1 class="text-4xl font-bold text-gray-900 mb-2">404</h1>
|
|
83
|
+
<h2 class="text-lg font-semibold text-gray-700 mb-3"><%= t("ruby_cms.errors.page_not_found", default: "Page Not Found") %></h2>
|
|
84
|
+
|
|
85
|
+
<p class="text-sm text-gray-500 mb-6">
|
|
86
|
+
<%= t("ruby_cms.errors.not_found_message", default: "The page you are looking for doesn't exist or has been moved.") %>
|
|
87
|
+
</p>
|
|
88
|
+
|
|
89
|
+
<div class="flex items-center gap-2">
|
|
90
|
+
<%= link_to t("ruby_cms.errors.back_to_admin", default: "Back to Admin"), ruby_cms_admin_root_path, class: "inline-flex h-9 items-center justify-center rounded-md border border-gray-200 bg-white px-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 transition-colors no-underline" %>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
data/config/database.yml
ADDED
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Importmap pins for the RubyCms engine.
|
|
4
|
+
#
|
|
5
|
+
# This file is loaded by the host application via:
|
|
6
|
+
# app.config.importmap.paths << RubyCms::Engine.root.join("config/importmap.rb")
|
|
7
|
+
#
|
|
8
|
+
# Ensure the engine's app/javascript is in config.assets.paths (done by the engine).
|
|
9
|
+
|
|
10
|
+
# Pin the main RubyCms controllers index for easy registration
|
|
11
|
+
pin "ruby_cms", to: "controllers/ruby_cms/index.js", preload: true
|
|
12
|
+
|
|
13
|
+
# Some downstream UI libraries (e.g. RubyUI) pin `@floating-ui/dom` to Skypack.
|
|
14
|
+
# Skypack intermittently returns 500s, which can prevent Stimulus controllers from
|
|
15
|
+
# loading (tooltip/select/etc) and break the Visual Editor experience.
|
|
16
|
+
#
|
|
17
|
+
# We pin it here (in the engine) to a more reliable ESM endpoint. Importmap pins
|
|
18
|
+
# are evaluated in order; later pins override earlier ones.
|
|
19
|
+
pin "@floating-ui/dom", to: "https://cdn.jsdelivr.net/npm/@floating-ui/dom/+esm"
|
|
20
|
+
|
|
21
|
+
# Alias pins for ruby_cms/ namespace (used by index.js imports)
|
|
22
|
+
pin "ruby_cms/visual_editor_controller", to: "controllers/ruby_cms/visual_editor_controller.js"
|
|
23
|
+
pin "ruby_cms/mobile_menu_controller", to: "controllers/ruby_cms/mobile_menu_controller.js"
|
|
24
|
+
pin "ruby_cms/flash_messages_controller", to: "controllers/ruby_cms/flash_messages_controller.js"
|
|
25
|
+
pin "ruby_cms/bulk_action_table_controller",
|
|
26
|
+
to: "controllers/ruby_cms/bulk_action_table_controller.js"
|
|
27
|
+
pin "ruby_cms/toggle_controller", to: "controllers/ruby_cms/toggle_controller.js"
|
|
28
|
+
pin "ruby_cms/locale_tabs_controller", to: "controllers/ruby_cms/locale_tabs_controller.js"
|
|
29
|
+
pin "ruby_cms/visual_editor_header_controller",
|
|
30
|
+
to: "controllers/ruby_cms/visual_editor_header_controller.js"
|
|
31
|
+
pin "ruby_cms/clickable_row_controller", to: "controllers/ruby_cms/clickable_row_controller.js"
|
|
32
|
+
pin "ruby_cms/auto_save_preference_controller",
|
|
33
|
+
to: "controllers/ruby_cms/auto_save_preference_controller.js"
|
|
34
|
+
pin "ruby_cms/nav_order_sortable_controller",
|
|
35
|
+
to: "controllers/ruby_cms/nav_order_sortable_controller.js"
|
|
36
|
+
pin "ruby_cms/page_preview_controller", to: "controllers/ruby_cms/page_preview_controller.js"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
en:
|
|
4
|
+
activemodel:
|
|
5
|
+
errors:
|
|
6
|
+
models:
|
|
7
|
+
ruby_cms/content_block:
|
|
8
|
+
attributes:
|
|
9
|
+
key:
|
|
10
|
+
reserved: "cannot start with a reserved prefix (e.g. admin_)"
|
|
11
|
+
image:
|
|
12
|
+
content_type_invalid: "must be PNG, JPEG, GIF, or WebP"
|
|
13
|
+
file_too_large: "must be under 5 MB"
|
|
14
|
+
|
|
15
|
+
ruby_cms:
|
|
16
|
+
nav:
|
|
17
|
+
settings: "Settings"
|
|
18
|
+
admin:
|
|
19
|
+
base:
|
|
20
|
+
authentication_required: "Authentication required."
|
|
21
|
+
not_authorized: "Not authorized."
|
|
22
|
+
locales:
|
|
23
|
+
en: "English"
|
|
24
|
+
nl: "Dutch"
|
|
25
|
+
de: "German"
|
|
26
|
+
fr: "French"
|
|
27
|
+
es: "Spanish"
|
|
28
|
+
settings:
|
|
29
|
+
title: "Settings"
|
|
30
|
+
subtitle: "Configure admin interface preferences"
|
|
31
|
+
pagination: "Pagination"
|
|
32
|
+
pagination_description: "Number of items to display per page in each table"
|
|
33
|
+
navigation: "Navigation"
|
|
34
|
+
navigation_description: "Control which pages appear in the navigation menu"
|
|
35
|
+
nav_order_label: "Menu order"
|
|
36
|
+
nav_order_hint: "Drag items to reorder the sidebar menu."
|
|
37
|
+
general: "General"
|
|
38
|
+
updated: "Setting '%{key}' updated successfully."
|
|
39
|
+
defaults_reset: "All settings have been reset to default values."
|
|
40
|
+
reset_defaults: "Reset to Defaults"
|
|
41
|
+
reset_confirm: "Reset all settings to default values?"
|
|
42
|
+
no_settings: "No settings in this category"
|
|
43
|
+
analytics:
|
|
44
|
+
title: "Analytics"
|
|
45
|
+
subtitle: "Traffic and engagement overview"
|
|
46
|
+
apply_filters: "Apply"
|
|
47
|
+
back_to_analytics: "Back to analytics"
|
|
48
|
+
page_details_title: "Page details"
|
|
49
|
+
visitor_details_title: "Visitor details"
|
|
50
|
+
total_page_views: "Total page views"
|
|
51
|
+
unique_visitors: "Unique visitors"
|
|
52
|
+
total_sessions: "Total sessions"
|
|
53
|
+
popular_pages: "Popular pages"
|
|
54
|
+
top_visitors: "Top visitors"
|
|
55
|
+
suspicious_activity: "Suspicious activity"
|
|
56
|
+
none_detected: "None detected in selected range."
|
|
57
|
+
no_data: "No data yet."
|
|
58
|
+
visitor_errors:
|
|
59
|
+
title: "Visitor errors"
|
|
60
|
+
item_name: "error"
|
|
61
|
+
time: "Time"
|
|
62
|
+
page: "Page"
|
|
63
|
+
error: "Error"
|
|
64
|
+
browser: "Browser"
|
|
65
|
+
ip: "IP"
|
|
66
|
+
status: "Status"
|
|
67
|
+
all: "All"
|
|
68
|
+
open: "Open"
|
|
69
|
+
resolved: "Resolved"
|
|
70
|
+
error_details: "Error details"
|
|
71
|
+
error_class: "Error class"
|
|
72
|
+
request_method: "Request method"
|
|
73
|
+
request_path: "Request path"
|
|
74
|
+
ip_address: "IP address"
|
|
75
|
+
occurred_at: "Occurred at"
|
|
76
|
+
error_message: "Error message"
|
|
77
|
+
codepath: "Code path"
|
|
78
|
+
stack_trace: "Stack trace"
|
|
79
|
+
request_parameters: "Request parameters"
|
|
80
|
+
referer: "Referer"
|
|
81
|
+
query_string: "Query string"
|
|
82
|
+
session_id: "Session ID"
|
|
83
|
+
user_agent: "User agent"
|
|
84
|
+
mark_as_resolved: "Mark as resolved"
|
|
85
|
+
mark_resolved_confirm: "Mark this error as resolved?"
|
|
86
|
+
back_to_errors: "Back to errors"
|
|
87
|
+
bulk_deleted: "%{count} error(s) deleted."
|
|
88
|
+
bulk_resolved: "%{count} error(s) marked as resolved."
|
|
89
|
+
content_blocks:
|
|
90
|
+
created: "Content block created."
|
|
91
|
+
updated: "Content block updated."
|
|
92
|
+
deleted: "Content block deleted."
|
|
93
|
+
locale_read_only: "(locale cannot be changed for existing blocks)"
|
|
94
|
+
select_locale: "Select locale"
|
|
95
|
+
edit_in_locale: "Edit in %{locale_name}"
|
|
96
|
+
other_languages: "Other languages"
|
|
97
|
+
content_type: "Content type"
|
|
98
|
+
title: "Title"
|
|
99
|
+
content: "Content"
|
|
100
|
+
rich_content: "Rich content"
|
|
101
|
+
published: "Published"
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RubyCms::Engine.routes.draw do # rubocop:disable Metrics/BlockLength
|
|
4
|
+
scope path: "admin", module: "ruby_cms/admin", as: "ruby_cms_admin" do # rubocop:disable Metrics/BlockLength
|
|
5
|
+
root to: "dashboard#index"
|
|
6
|
+
|
|
7
|
+
resources :content_blocks do
|
|
8
|
+
collection do
|
|
9
|
+
delete "bulk_delete", to: "content_blocks#bulk_delete"
|
|
10
|
+
patch "bulk_publish", to: "content_blocks#bulk_publish"
|
|
11
|
+
patch "bulk_unpublish", to: "content_blocks#bulk_unpublish"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
resources :permissions, only: %i[index create destroy] do
|
|
16
|
+
collection do
|
|
17
|
+
delete "bulk_delete", to: "permissions#bulk_delete"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
resources :visitor_errors, only: %i[index show] do
|
|
22
|
+
member do
|
|
23
|
+
patch :resolve
|
|
24
|
+
end
|
|
25
|
+
collection do
|
|
26
|
+
delete :bulk_delete, to: "visitor_errors#bulk_delete"
|
|
27
|
+
patch :bulk_mark_as_resolved, to: "visitor_errors#bulk_mark_as_resolved"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
resources :analytics, only: %i[index] do
|
|
32
|
+
collection do
|
|
33
|
+
get :page_details
|
|
34
|
+
get :visitor_details
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
get "settings", to: "settings#index", as: :settings
|
|
39
|
+
patch "settings", to: "settings#update", as: nil
|
|
40
|
+
post "settings", to: "settings#update", as: nil
|
|
41
|
+
post "settings/nav_order", to: "settings#update_nav_order", as: :settings_nav_order
|
|
42
|
+
post "settings/reset_defaults", to: "settings#reset_defaults", as: :settings_reset_defaults
|
|
43
|
+
|
|
44
|
+
resources :users, only: %i[index create destroy] do
|
|
45
|
+
collection do
|
|
46
|
+
delete "bulk_delete", to: "users#bulk_delete"
|
|
47
|
+
end
|
|
48
|
+
resources :permissions, only: %i[index create destroy], controller: "user_permissions",
|
|
49
|
+
path: "permissions" do
|
|
50
|
+
collection do
|
|
51
|
+
delete "bulk_delete", to: "user_permissions#bulk_delete"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Visual editor routes
|
|
57
|
+
get "visual_editor", to: "visual_editor#index", as: :visual_editor
|
|
58
|
+
get "visual_editor/page_preview", to: "visual_editor#page_preview",
|
|
59
|
+
as: :visual_editor_page_preview
|
|
60
|
+
patch "visual_editor/quick_update", to: "visual_editor#quick_update",
|
|
61
|
+
as: :visual_editor_quick_update
|
|
62
|
+
|
|
63
|
+
patch "locale", to: "locale#update", as: :locale
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateRubyCmsPermissions < ActiveRecord::Migration[7.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ruby_cms_permissions do |t|
|
|
6
|
+
t.string :key, null: false
|
|
7
|
+
t.string :name
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index :ruby_cms_permissions, :key, unique: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateRubyCmsUserPermissions < ActiveRecord::Migration[7.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ruby_cms_user_permissions do |t|
|
|
6
|
+
t.references :user, null: false, foreign_key: false
|
|
7
|
+
t.references :permission, null: false, foreign_key: { to_table: :ruby_cms_permissions }
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index :ruby_cms_user_permissions, %i[user_id permission_id], unique: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateRubyCmsContentBlocks < ActiveRecord::Migration[7.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ruby_cms_content_blocks do |t|
|
|
6
|
+
t.string :key, null: false
|
|
7
|
+
t.string :title
|
|
8
|
+
t.text :content
|
|
9
|
+
t.string :content_type, default: "text", null: false
|
|
10
|
+
t.boolean :published, default: false, null: false
|
|
11
|
+
t.references :updated_by, null: true, foreign_key: false
|
|
12
|
+
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
add_index :ruby_cms_content_blocks, :key, unique: true
|
|
17
|
+
add_index :ruby_cms_content_blocks, %i[published content_type]
|
|
18
|
+
end
|
|
19
|
+
end
|