lean_cms 0.2.12
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/CHANGELOG.md +235 -0
- data/LICENSE +21 -0
- data/README.md +107 -0
- data/app/assets/images/lean_cms/sloth-404.png +0 -0
- data/app/assets/images/lean_cms/sloth-500.png +0 -0
- data/app/assets/images/lean_cms/sloth-favicon-16.png +0 -0
- data/app/assets/images/lean_cms/sloth-favicon-32.png +0 -0
- data/app/assets/images/lean_cms/sloth-favicon-64.png +0 -0
- data/app/assets/images/lean_cms/sloth-logo.png +0 -0
- data/app/assets/lean_cms/actiontext.css +440 -0
- data/app/assets/lean_cms/cms_edit_controls.css +548 -0
- data/app/assets/tailwind/lean_cms/engine.css +14 -0
- data/app/components/lean_cms/base_component.rb +61 -0
- data/app/components/lean_cms/bullets_section_component.html.erb +23 -0
- data/app/components/lean_cms/bullets_section_component.rb +54 -0
- data/app/components/lean_cms/cards_section_component.html.erb +237 -0
- data/app/components/lean_cms/cards_section_component.rb +71 -0
- data/app/components/lean_cms/editable_content_component.html.erb +15 -0
- data/app/components/lean_cms/editable_content_component.rb +53 -0
- data/app/components/lean_cms/section_component.html.erb +18 -0
- data/app/components/lean_cms/section_component.rb +35 -0
- data/app/controllers/concerns/lean_cms/authentication.rb +60 -0
- data/app/controllers/concerns/lean_cms/authorization.rb +60 -0
- data/app/controllers/lean_cms/activity_controller.rb +16 -0
- data/app/controllers/lean_cms/application_controller.rb +48 -0
- data/app/controllers/lean_cms/dashboard_controller.rb +13 -0
- data/app/controllers/lean_cms/form_submissions_controller.rb +37 -0
- data/app/controllers/lean_cms/notification_settings_controller.rb +145 -0
- data/app/controllers/lean_cms/notifications_controller.rb +26 -0
- data/app/controllers/lean_cms/page_contents_controller.rb +403 -0
- data/app/controllers/lean_cms/password_setup_controller.rb +65 -0
- data/app/controllers/lean_cms/passwords_controller.rb +42 -0
- data/app/controllers/lean_cms/posts_controller.rb +78 -0
- data/app/controllers/lean_cms/sessions_controller.rb +50 -0
- data/app/controllers/lean_cms/settings_controller.rb +124 -0
- data/app/controllers/lean_cms/users_controller.rb +113 -0
- data/app/helpers/lean_cms/activity_helper.rb +190 -0
- data/app/helpers/lean_cms/application_helper.rb +43 -0
- data/app/helpers/lean_cms/content_helper.rb +34 -0
- data/app/helpers/lean_cms/page_content_helper.rb +359 -0
- data/app/javascript/controllers/cards_editor_controller.js +317 -0
- data/app/javascript/controllers/cms_sticky_overlay_controller.js +59 -0
- data/app/javascript/controllers/field_editor_form_controller.js +68 -0
- data/app/javascript/controllers/field_editor_modal_controller.js +79 -0
- data/app/javascript/controllers/inline_edit_controller.js +414 -0
- data/app/javascript/controllers/inline_edit_toggle_controller.js +81 -0
- data/app/javascript/controllers/notifications_controller.js +19 -0
- data/app/javascript/controllers/settings_inline_edit_sync_controller.js +38 -0
- data/app/javascript/controllers/settings_override_controller.js +45 -0
- data/app/mailers/lean_cms/application_mailer.rb +6 -0
- data/app/mailers/lean_cms/passwords_mailer.rb +8 -0
- data/app/mailers/lean_cms/users_mailer.rb +39 -0
- data/app/models/lean_cms/current.rb +6 -0
- data/app/models/lean_cms/form_submission.rb +45 -0
- data/app/models/lean_cms/magic_link.rb +76 -0
- data/app/models/lean_cms/meta_tag.rb +30 -0
- data/app/models/lean_cms/notification_setting.rb +69 -0
- data/app/models/lean_cms/page.rb +23 -0
- data/app/models/lean_cms/page_content.rb +245 -0
- data/app/models/lean_cms/post.rb +65 -0
- data/app/models/lean_cms/session.rb +7 -0
- data/app/models/lean_cms/setting.rb +156 -0
- data/app/policies/lean_cms/application_policy.rb +35 -0
- data/app/policies/lean_cms/page_content_policy.rb +31 -0
- data/app/policies/lean_cms/post_policy.rb +37 -0
- data/app/policies/lean_cms/setting_policy.rb +17 -0
- data/app/views/layouts/lean_cms/application.html.erb +114 -0
- data/app/views/layouts/lean_cms/auth.html.erb +200 -0
- data/app/views/lean_cms/activity/index.html.erb +79 -0
- data/app/views/lean_cms/dashboard/index.html.erb +180 -0
- data/app/views/lean_cms/form_submissions/index.html.erb +104 -0
- data/app/views/lean_cms/form_submissions/show.html.erb +157 -0
- data/app/views/lean_cms/notification_settings/edit.html.erb +192 -0
- data/app/views/lean_cms/notifications/index.html.erb +72 -0
- data/app/views/lean_cms/notifications/show.html.erb +39 -0
- data/app/views/lean_cms/page_contents/_field_editor.html.erb +174 -0
- data/app/views/lean_cms/page_contents/edit.html.erb +428 -0
- data/app/views/lean_cms/page_contents/index.html.erb +113 -0
- data/app/views/lean_cms/password_setup/show.html.erb +35 -0
- data/app/views/lean_cms/passwords/edit.html.erb +26 -0
- data/app/views/lean_cms/passwords/new.html.erb +21 -0
- data/app/views/lean_cms/passwords_mailer/reset.html.erb +6 -0
- data/app/views/lean_cms/passwords_mailer/reset.text.erb +4 -0
- data/app/views/lean_cms/posts/_form.html.erb +118 -0
- data/app/views/lean_cms/posts/edit.html.erb +31 -0
- data/app/views/lean_cms/posts/index.html.erb +100 -0
- data/app/views/lean_cms/posts/new.html.erb +16 -0
- data/app/views/lean_cms/sessions/new.html.erb +28 -0
- data/app/views/lean_cms/settings/edit.html.erb +384 -0
- data/app/views/lean_cms/shared/_admin_bar.html.erb +85 -0
- data/app/views/lean_cms/shared/_header.html.erb +86 -0
- data/app/views/lean_cms/shared/_notifications_bell.html.erb +84 -0
- data/app/views/lean_cms/shared/_sidebar.html.erb +102 -0
- data/app/views/lean_cms/users/_form.html.erb +105 -0
- data/app/views/lean_cms/users/edit.html.erb +8 -0
- data/app/views/lean_cms/users/index.html.erb +99 -0
- data/app/views/lean_cms/users/new.html.erb +8 -0
- data/app/views/lean_cms/users_mailer/admin_triggered_password_reset.html.erb +13 -0
- data/app/views/lean_cms/users_mailer/admin_triggered_password_reset.text.erb +11 -0
- data/app/views/lean_cms/users_mailer/invitation.html.erb +13 -0
- data/app/views/lean_cms/users_mailer/invitation.text.erb +11 -0
- data/app/views/lean_cms/users_mailer/reactivation.html.erb +13 -0
- data/app/views/lean_cms/users_mailer/reactivation.text.erb +11 -0
- data/config/importmap.rb +8 -0
- data/config/routes.rb +78 -0
- data/db/migrate/20251112034030_create_lean_cms_tables.rb +131 -0
- data/db/migrate/20260513000001_create_lean_cms_auth_tables.rb +31 -0
- data/db/migrate/20260514000001_create_paper_trail_versions.rb +16 -0
- data/db/migrate/20260514000002_create_action_text_tables.rb +18 -0
- data/db/migrate/20260514000003_create_active_storage_tables.rb +45 -0
- data/db/migrate/20260514000004_create_noticed_tables.rb +27 -0
- data/lib/generators/lean_cms/demo/demo_generator.rb +54 -0
- data/lib/generators/lean_cms/demo/templates/lean_cms_structure.yml +129 -0
- data/lib/generators/lean_cms/demo/templates/pages_controller.rb +30 -0
- data/lib/generators/lean_cms/demo/templates/views/pages/about.html.erb +40 -0
- data/lib/generators/lean_cms/demo/templates/views/pages/contact.html.erb +55 -0
- data/lib/generators/lean_cms/demo/templates/views/pages/home.html.erb +31 -0
- data/lib/generators/lean_cms/install/install_generator.rb +317 -0
- data/lib/generators/lean_cms/install/templates/add_lean_cms_columns_to_users.rb.tt +7 -0
- data/lib/generators/lean_cms/install/templates/lean_cms.rb +11 -0
- data/lib/generators/lean_cms/install/templates/lean_cms_structure.yml +29 -0
- data/lib/lean_cms/configuration.rb +32 -0
- data/lib/lean_cms/engine.rb +93 -0
- data/lib/lean_cms/loader.rb +217 -0
- data/lib/lean_cms/sync_helper.rb +182 -0
- data/lib/lean_cms/version.rb +3 -0
- data/lib/lean_cms.rb +26 -0
- data/lib/tasks/lean_cms.rake +390 -0
- metadata +313 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<div class="max-w-4xl">
|
|
2
|
+
<div class="mb-8">
|
|
3
|
+
<h1 class="text-3xl font-bold text-gray-900">Notification Settings</h1>
|
|
4
|
+
<p class="text-gray-600 mt-2">Configure how you receive notifications for contact form submissions</p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<%= form_with url: lean_cms_notification_settings_path, method: :patch, class: "space-y-6" do |f| %>
|
|
8
|
+
|
|
9
|
+
<!-- Email Notifications -->
|
|
10
|
+
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6" data-controller="toggle-fields">
|
|
11
|
+
<div class="flex items-start mb-4">
|
|
12
|
+
<div class="flex items-center h-5">
|
|
13
|
+
<%= check_box_tag 'email_enabled', '1', @settings.email_enabled?,
|
|
14
|
+
class: "w-4 h-4 text-[#b82025] bg-gray-100 border-gray-300 rounded focus:ring-[#b82025] focus:ring-2",
|
|
15
|
+
data: { action: "change->toggle-fields#toggle" } %>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="ml-3 flex-1">
|
|
18
|
+
<label for="email_enabled" class="font-semibold text-gray-900 text-lg">
|
|
19
|
+
Email Notifications
|
|
20
|
+
</label>
|
|
21
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
22
|
+
Receive email notifications when contact form submissions are received. You'll receive the full form details.
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div data-toggle-fields-target="fields" class="<%= 'hidden' unless @settings.email_enabled? %> space-y-4 mt-4 pt-4 border-t border-gray-200" data-controller="email-provider">
|
|
28
|
+
<!-- Email Provider Selection -->
|
|
29
|
+
<div>
|
|
30
|
+
<label for="email_provider" class="block text-sm font-medium text-gray-700 mb-2">Email Provider</label>
|
|
31
|
+
<div class="space-y-2">
|
|
32
|
+
<div class="flex items-center">
|
|
33
|
+
<%= radio_button_tag 'email_provider', 'sendgrid', @settings.email_provider == 'sendgrid',
|
|
34
|
+
class: "w-4 h-4 text-[#b82025] bg-gray-100 border-gray-300 focus:ring-[#b82025] focus:ring-2",
|
|
35
|
+
data: { action: "change->email-provider#change" } %>
|
|
36
|
+
<label for="email_provider_sendgrid" class="ml-2 text-sm font-medium text-gray-700">Sendgrid</label>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="flex items-center">
|
|
39
|
+
<%= radio_button_tag 'email_provider', 'mailgun', @settings.email_provider == 'mailgun',
|
|
40
|
+
class: "w-4 h-4 text-[#b82025] bg-gray-100 border-gray-300 focus:ring-[#b82025] focus:ring-2",
|
|
41
|
+
data: { action: "change->email-provider#change" } %>
|
|
42
|
+
<label for="email_provider_mailgun" class="ml-2 text-sm font-medium text-gray-700">Mailgun</label>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="flex items-center">
|
|
45
|
+
<%= radio_button_tag 'email_provider', 'none', @settings.email_provider == 'none' || @settings.email_provider.blank?,
|
|
46
|
+
class: "w-4 h-4 text-[#b82025] bg-gray-100 border-gray-300 focus:ring-[#b82025] focus:ring-2",
|
|
47
|
+
data: { action: "change->email-provider#change" } %>
|
|
48
|
+
<label for="email_provider_none" class="ml-2 text-sm font-medium text-gray-700">None</label>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<!-- Sendgrid Credentials -->
|
|
54
|
+
<div data-email-provider-target="sendgridFields" class="<%= 'hidden' unless @settings.email_provider == 'sendgrid' %> space-y-4 mt-4">
|
|
55
|
+
<div>
|
|
56
|
+
<label for="sendgrid_api_key" class="block text-sm font-medium text-gray-700 mb-1">Sendgrid API Key</label>
|
|
57
|
+
<%= password_field_tag :sendgrid_api_key, '',
|
|
58
|
+
placeholder: @settings.sendgrid_api_key.present? ? "Enter new key or leave blank to keep existing" : "Enter your Sendgrid API key",
|
|
59
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
60
|
+
<p class="text-xs text-gray-500 mt-1"><%= @settings.sendgrid_api_key.present? ? "Key is set. Enter a new value to change it." : "Enter your Sendgrid API key" %></p>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<!-- Mailgun Credentials -->
|
|
65
|
+
<div data-email-provider-target="mailgunFields" class="<%= 'hidden' unless @settings.email_provider == 'mailgun' %> space-y-4 mt-4">
|
|
66
|
+
<div>
|
|
67
|
+
<label for="mailgun_api_key" class="block text-sm font-medium text-gray-700 mb-1">Mailgun API Key</label>
|
|
68
|
+
<%= password_field_tag :mailgun_api_key, '',
|
|
69
|
+
placeholder: @settings.mailgun_api_key.present? ? "Enter new key or leave blank to keep existing" : "Enter your Mailgun API key",
|
|
70
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
71
|
+
<p class="text-xs text-gray-500 mt-1"><%= @settings.mailgun_api_key.present? ? "Key is set. Enter a new value to change it." : "Enter your Mailgun API key" %></p>
|
|
72
|
+
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<label for="mailgun_domain" class="block text-sm font-medium text-gray-700 mb-1">Mailgun Domain</label>
|
|
75
|
+
<%= text_field_tag :mailgun_domain, @settings.mailgun_domain,
|
|
76
|
+
placeholder: "e.g., mg.example.com",
|
|
77
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<!-- Notification Email Recipients -->
|
|
82
|
+
<div>
|
|
83
|
+
<label for="notification_emails" class="block text-sm font-medium text-gray-700 mb-1">Recipient Email Addresses</label>
|
|
84
|
+
<%= text_field_tag :notification_emails, @settings.notification_email_list.join(', '),
|
|
85
|
+
placeholder: "email1@example.com, email2@example.com",
|
|
86
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
87
|
+
<p class="text-xs text-gray-500 mt-1">Separate multiple emails with commas</p>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Test Email Button -->
|
|
91
|
+
<div class="pt-2">
|
|
92
|
+
<%= link_to "Send Test Email", test_email_lean_cms_notification_settings_path,
|
|
93
|
+
method: :post,
|
|
94
|
+
class: "inline-flex items-center px-4 py-2 text-sm font-medium text-[#b82025] border border-[#b82025] rounded-lg hover:bg-[#b82025] hover:text-white transition-colors",
|
|
95
|
+
data: { confirm: "This will send a test notification. Continue?" } %>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- SMS Notifications -->
|
|
101
|
+
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6" data-controller="toggle-fields">
|
|
102
|
+
<div class="flex items-start mb-4">
|
|
103
|
+
<div class="flex items-center h-5">
|
|
104
|
+
<%= check_box_tag 'sms_enabled', '1', @settings.sms_enabled?,
|
|
105
|
+
class: "w-4 h-4 text-[#b82025] bg-gray-100 border-gray-300 rounded focus:ring-[#b82025] focus:ring-2",
|
|
106
|
+
data: { action: "change->toggle-fields#toggle" } %>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="ml-3 flex-1">
|
|
109
|
+
<label for="sms_enabled" class="font-semibold text-gray-900 text-lg">
|
|
110
|
+
SMS Notifications (Twilio)
|
|
111
|
+
</label>
|
|
112
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
113
|
+
Receive SMS text messages when contact form submissions are received. You'll receive a brief notification.
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div data-toggle-fields-target="fields" class="<%= 'hidden' unless @settings.sms_enabled? %> space-y-4 mt-4 pt-4 border-t border-gray-200">
|
|
119
|
+
<!-- Twilio Credentials -->
|
|
120
|
+
<div>
|
|
121
|
+
<label for="twilio_account_sid" class="block text-sm font-medium text-gray-700 mb-1">Twilio Account SID</label>
|
|
122
|
+
<%= password_field_tag :twilio_account_sid, '',
|
|
123
|
+
placeholder: @settings.twilio_account_sid.present? ? "Enter new SID or leave blank to keep existing" : "Enter your Twilio Account SID",
|
|
124
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
125
|
+
<p class="text-xs text-gray-500 mt-1"><%= @settings.twilio_account_sid.present? ? "SID is set. Enter a new value to change it." : "Enter your Twilio Account SID" %></p>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div>
|
|
129
|
+
<label for="twilio_auth_token" class="block text-sm font-medium text-gray-700 mb-1">Twilio Auth Token</label>
|
|
130
|
+
<%= password_field_tag :twilio_auth_token, '',
|
|
131
|
+
placeholder: @settings.twilio_auth_token.present? ? "Enter new token or leave blank to keep existing" : "Enter your Twilio Auth Token",
|
|
132
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
133
|
+
<p class="text-xs text-gray-500 mt-1"><%= @settings.twilio_auth_token.present? ? "Token is set. Enter a new value to change it." : "Enter your Twilio Auth Token" %></p>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div>
|
|
137
|
+
<label for="twilio_from_number" class="block text-sm font-medium text-gray-700 mb-1">Twilio From Number</label>
|
|
138
|
+
<%= text_field_tag :twilio_from_number, @settings.twilio_from_number,
|
|
139
|
+
placeholder: "+1234567890",
|
|
140
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
141
|
+
<p class="text-xs text-gray-500 mt-1">The phone number your SMS will be sent from (must be verified in Twilio)</p>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<!-- Notification Phone Recipients -->
|
|
145
|
+
<div>
|
|
146
|
+
<label for="notification_phones" class="block text-sm font-medium text-gray-700 mb-1">Recipient Phone Numbers</label>
|
|
147
|
+
<%= text_field_tag :notification_phones, @settings.notification_phone_list.join(', '),
|
|
148
|
+
placeholder: "+1234567890, +1987654321",
|
|
149
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#b82025]/20 focus:border-[#b82025] outline-none" %>
|
|
150
|
+
<p class="text-xs text-gray-500 mt-1">Separate multiple phone numbers with commas. Include country code (e.g., +1 for US)</p>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- Test SMS Button -->
|
|
154
|
+
<div class="pt-2">
|
|
155
|
+
<%= link_to "Send Test SMS", test_sms_lean_cms_notification_settings_path,
|
|
156
|
+
method: :post,
|
|
157
|
+
class: "inline-flex items-center px-4 py-2 text-sm font-medium text-[#b82025] border border-[#b82025] rounded-lg hover:bg-[#b82025] hover:text-white transition-colors",
|
|
158
|
+
data: { confirm: "This will send a test SMS notification. Continue?" } %>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<!-- In-App Notifications -->
|
|
164
|
+
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
165
|
+
<div class="flex items-start">
|
|
166
|
+
<div class="flex items-center h-5">
|
|
167
|
+
<%= check_box_tag 'in_app_enabled', '1', @settings.in_app_enabled?,
|
|
168
|
+
class: "w-4 h-4 text-[#b82025] bg-gray-100 border-gray-300 rounded focus:ring-[#b82025] focus:ring-2" %>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="ml-3 flex-1">
|
|
171
|
+
<label for="in_app_enabled" class="font-semibold text-gray-900 text-lg">
|
|
172
|
+
In-App Notifications
|
|
173
|
+
</label>
|
|
174
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
175
|
+
Show notifications in the CMS admin panel when contact form submissions are received. These appear in the notification bell icon.
|
|
176
|
+
</p>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<!-- Save Button -->
|
|
182
|
+
<div class="flex items-center justify-between pt-4">
|
|
183
|
+
<div class="text-sm text-gray-600">
|
|
184
|
+
Changes take effect immediately after saving
|
|
185
|
+
</div>
|
|
186
|
+
<div class="flex items-center gap-3">
|
|
187
|
+
<%= link_to "Cancel", lean_cms_root_path, class: "px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg transition-colors" %>
|
|
188
|
+
<%= f.submit "Save Settings", class: "px-6 py-2 bg-[#b82025] hover:bg-[#a01c20] text-white font-semibold rounded-lg transition-colors cursor-pointer" %>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
<% end %>
|
|
192
|
+
</div>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<% content_for :page_title, "Notifications" %>
|
|
2
|
+
|
|
3
|
+
<div class="max-w-4xl">
|
|
4
|
+
<div class="mb-8 flex items-center justify-between">
|
|
5
|
+
<div>
|
|
6
|
+
<h1 class="text-3xl font-bold text-gray-900">Notifications</h1>
|
|
7
|
+
<p class="text-gray-600 mt-2">
|
|
8
|
+
<% if @unread_count > 0 %>
|
|
9
|
+
<%= pluralize(@unread_count, 'unread notification') %>
|
|
10
|
+
<% else %>
|
|
11
|
+
All caught up!
|
|
12
|
+
<% end %>
|
|
13
|
+
</p>
|
|
14
|
+
</div>
|
|
15
|
+
<% if @unread_count > 0 %>
|
|
16
|
+
<%= button_to mark_all_as_read_lean_cms_notifications_path,
|
|
17
|
+
method: :patch,
|
|
18
|
+
class: "px-4 py-2 text-sm font-medium text-[#b82025] border border-[#b82025] rounded-lg hover:bg-[#b82025] hover:text-white transition-colors" do %>
|
|
19
|
+
Mark All as Read
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
|
|
25
|
+
<% if @notifications.any? %>
|
|
26
|
+
<div class="divide-y divide-gray-200">
|
|
27
|
+
<% @notifications.each do |notification| %>
|
|
28
|
+
<%= link_to lean_cms_notification_path(notification),
|
|
29
|
+
class: "block px-6 py-4 hover:bg-gray-50 transition-colors #{'bg-blue-50' unless notification.read?}" do %>
|
|
30
|
+
<div class="flex items-start">
|
|
31
|
+
<div class="flex-shrink-0">
|
|
32
|
+
<% unless notification.read? %>
|
|
33
|
+
<div class="h-2 w-2 rounded-full bg-[#b82025] mt-2"></div>
|
|
34
|
+
<% end %>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="ml-3 flex-1 min-w-0">
|
|
37
|
+
<div class="flex items-center justify-between">
|
|
38
|
+
<p class="text-sm font-medium text-gray-900">
|
|
39
|
+
<%= notification.params[:title] || 'Contact Form Submission' %>
|
|
40
|
+
</p>
|
|
41
|
+
<span class="text-xs text-gray-400">
|
|
42
|
+
<%= time_ago_in_words(notification.created_at) %> ago
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
46
|
+
<%= notification.params[:message] || notification.message %>
|
|
47
|
+
</p>
|
|
48
|
+
<% if notification.params[:url] %>
|
|
49
|
+
<p class="text-xs text-[#b82025] mt-2">
|
|
50
|
+
View details →
|
|
51
|
+
</p>
|
|
52
|
+
<% end %>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% end %>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="px-6 py-4 border-t border-gray-200">
|
|
60
|
+
<%= paginate @notifications %>
|
|
61
|
+
</div>
|
|
62
|
+
<% else %>
|
|
63
|
+
<div class="px-6 py-12 text-center">
|
|
64
|
+
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
65
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
|
|
66
|
+
</svg>
|
|
67
|
+
<p class="mt-2 text-sm text-gray-600">No notifications yet</p>
|
|
68
|
+
<p class="mt-1 text-xs text-gray-500">You'll see notifications here when contact form submissions are received</p>
|
|
69
|
+
</div>
|
|
70
|
+
<% end %>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<% content_for :page_title, "Notification Details" %>
|
|
2
|
+
|
|
3
|
+
<div class="max-w-4xl">
|
|
4
|
+
<div class="mb-6">
|
|
5
|
+
<%= link_to lean_cms_notifications_path, class: "inline-flex items-center text-sm text-gray-600 hover:text-gray-900 mb-4" do %>
|
|
6
|
+
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
7
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
|
8
|
+
</svg>
|
|
9
|
+
Back to Notifications
|
|
10
|
+
<% end %>
|
|
11
|
+
<h1 class="text-3xl font-bold text-gray-900">
|
|
12
|
+
<%= @notification.params[:title] || 'Contact Form Submission' %>
|
|
13
|
+
</h1>
|
|
14
|
+
<p class="text-gray-600 mt-2">
|
|
15
|
+
<%= time_ago_in_words(@notification.created_at) %> ago
|
|
16
|
+
</p>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
20
|
+
<div class="prose max-w-none">
|
|
21
|
+
<p class="text-gray-700">
|
|
22
|
+
<%= @notification.params[:message] || @notification.message %>
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<% if @notification.params[:url] %>
|
|
27
|
+
<div class="mt-6 pt-6 border-t border-gray-200">
|
|
28
|
+
<%= link_to @notification.params[:url],
|
|
29
|
+
class: "inline-flex items-center px-4 py-2 bg-[#b82025] hover:bg-[#a01c20] text-white font-medium rounded-lg transition-colors",
|
|
30
|
+
data: { turbo_frame: "_top" } do %>
|
|
31
|
+
View Details
|
|
32
|
+
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
33
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
34
|
+
</svg>
|
|
35
|
+
<% end %>
|
|
36
|
+
</div>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<div class="cms-field-editor-modal">
|
|
2
|
+
<div class="flex items-center justify-between mb-6 pb-4 border-b border-gray-200">
|
|
3
|
+
<div>
|
|
4
|
+
<h2 class="text-2xl font-bold text-gray-900"><%= field.label || field.key.humanize %></h2>
|
|
5
|
+
<p class="text-sm text-gray-600 mt-1"><%= field.page&.title || field.read_attribute(:page)&.titleize %> › <%= field.section.humanize %></p>
|
|
6
|
+
</div>
|
|
7
|
+
<button type="button" data-action="click->field-editor-modal#close"
|
|
8
|
+
class="cursor-pointer text-gray-400 hover:text-gray-600">
|
|
9
|
+
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
10
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
11
|
+
</svg>
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<%= form_with url: lean_cms_update_page_content_field_path(field.id),
|
|
16
|
+
method: :patch,
|
|
17
|
+
multipart: true,
|
|
18
|
+
data: { controller: 'field-editor-form', action: 'submit->field-editor-form#handleSubmit' } do |f| %>
|
|
19
|
+
|
|
20
|
+
<div class="space-y-4">
|
|
21
|
+
<% case field.content_type.to_sym %>
|
|
22
|
+
<% when :rich_text %>
|
|
23
|
+
<%# display_value for rich_text returns the FULL rendered partial
|
|
24
|
+
(action_text/contents/_content.html.erb), which wraps the body
|
|
25
|
+
in a <div class="trix-content"> and ActionText template
|
|
26
|
+
comments. Trix can't initialize from that — it needs the raw
|
|
27
|
+
inner HTML body. Read it off the underlying rich_text record. %>
|
|
28
|
+
<%= rich_text_area_tag 'value', field.rich_content&.body&.to_html.to_s,
|
|
29
|
+
class: 'w-full min-h-[300px] border border-gray-300 rounded-lg' %>
|
|
30
|
+
|
|
31
|
+
<% when :bullets %>
|
|
32
|
+
<%
|
|
33
|
+
bullets = field.display_value.is_a?(Array) ? field.display_value : []
|
|
34
|
+
bullets = [''] if bullets.empty?
|
|
35
|
+
%>
|
|
36
|
+
<div data-controller="dynamic-list" data-dynamic-list-max-value="20" data-dynamic-list-min-value="1">
|
|
37
|
+
<div data-dynamic-list-target="list" class="space-y-2">
|
|
38
|
+
<% bullets.each do |bullet| %>
|
|
39
|
+
<div data-list-item class="flex gap-3 items-center">
|
|
40
|
+
<%= text_field_tag "bullet_items[]", bullet,
|
|
41
|
+
placeholder: "Enter a bullet point",
|
|
42
|
+
class: "flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 outline-none" %>
|
|
43
|
+
<button type="button" data-action="click->dynamic-list#remove"
|
|
44
|
+
class="p-2 text-gray-400 hover:text-red-500 transition-colors cursor-pointer flex-shrink-0">
|
|
45
|
+
<svg class="w-5 h-5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
46
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
|
47
|
+
</svg>
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
<% end %>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<button type="button" data-action="click->dynamic-list#add"
|
|
54
|
+
class="mt-3 inline-flex items-center text-sm font-medium text-blue-600 hover:text-blue-700 transition-colors cursor-pointer">
|
|
55
|
+
<svg class="w-4 h-4 mr-1 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
56
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
|
57
|
+
</svg>
|
|
58
|
+
Add Bullet Point
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
<template data-dynamic-list-target="template">
|
|
62
|
+
<div data-list-item class="flex gap-3 items-center">
|
|
63
|
+
<%= text_field_tag "bullet_items[]", "",
|
|
64
|
+
placeholder: "Enter a bullet point",
|
|
65
|
+
class: "flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 outline-none" %>
|
|
66
|
+
<button type="button" data-action="click->dynamic-list#remove"
|
|
67
|
+
class="p-2 text-gray-400 hover:text-red-500 transition-colors cursor-pointer flex-shrink-0">
|
|
68
|
+
<svg class="w-5 h-5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
69
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
|
70
|
+
</svg>
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
</template>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<!-- Hidden field to store the bullets field ID for controller processing -->
|
|
77
|
+
<input type="hidden" name="bullets_field_id" value="<%= field.id %>">
|
|
78
|
+
|
|
79
|
+
<% when :cards %>
|
|
80
|
+
<% cards = field.display_value.is_a?(Array) ? field.display_value : [] %>
|
|
81
|
+
<%# Add image URLs for existing images so they show in the editor %>
|
|
82
|
+
<% cards_with_urls = cards.map do |card|
|
|
83
|
+
card_hash = card.except('image_attachment').to_h.with_indifferent_access
|
|
84
|
+
if card_hash['image_id'].present? && field.card_images.attached?
|
|
85
|
+
begin
|
|
86
|
+
img = field.card_image(card_hash['image_id'])
|
|
87
|
+
if img.present? && img.blob.present?
|
|
88
|
+
card_hash['image_url'] = rails_blob_path(img, only_path: true)
|
|
89
|
+
end
|
|
90
|
+
rescue => e
|
|
91
|
+
Rails.logger.warn "Could not load card image: #{e.message}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
card_hash
|
|
95
|
+
end %>
|
|
96
|
+
<div data-controller="cards-editor" data-cards-editor-initial-value="<%= h(cards_with_urls.to_json) %>" class="space-y-4" id="cards-editor-<%= field.id %>">
|
|
97
|
+
<!-- Cards List Container -->
|
|
98
|
+
<div data-cards-editor-target="cardsList" class="space-y-3">
|
|
99
|
+
<!-- Cards will be rendered here by JavaScript -->
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<!-- Add Card Button -->
|
|
103
|
+
<button type="button" data-action="click->cards-editor#addCard"
|
|
104
|
+
class="w-full px-4 py-2 border-2 border-dashed border-gray-300 rounded-lg text-sm font-medium text-gray-600 hover:border-gray-400 hover:text-gray-700 transition-colors cursor-pointer">
|
|
105
|
+
+ Add Card
|
|
106
|
+
</button>
|
|
107
|
+
|
|
108
|
+
<!-- Hidden input to store JSON -->
|
|
109
|
+
<input type="hidden" name="value" data-cards-editor-target="hiddenInput">
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<% when :image %>
|
|
113
|
+
<div class="space-y-3">
|
|
114
|
+
<% if field.image_file.attached? %>
|
|
115
|
+
<div class="relative inline-block">
|
|
116
|
+
<%= image_tag field.image_file, class: "max-w-xs rounded-lg border border-gray-200" %>
|
|
117
|
+
<div class="mt-2 text-sm text-gray-600">
|
|
118
|
+
Current image: <%= field.image_file.filename %>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
<% elsif field.value.present? %>
|
|
122
|
+
<div class="text-sm text-gray-600">
|
|
123
|
+
Current URL: <%= field.value %>
|
|
124
|
+
</div>
|
|
125
|
+
<% end %>
|
|
126
|
+
|
|
127
|
+
<%= file_field_tag "image_file",
|
|
128
|
+
accept: "image/*",
|
|
129
|
+
class: "block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 focus:outline-none" %>
|
|
130
|
+
<p class="text-xs text-gray-500">Upload a new image to replace the current one</p>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<% when :boolean %>
|
|
134
|
+
<div class="flex items-center">
|
|
135
|
+
<%= check_box_tag 'value', "1", field.display_value,
|
|
136
|
+
class: "w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2" %>
|
|
137
|
+
<span class="ml-2 text-sm text-gray-600">Enable this feature</span>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<% when :url %>
|
|
141
|
+
<%= url_field_tag 'value', field.display_value,
|
|
142
|
+
class: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
143
|
+
placeholder: "https://example.com" %>
|
|
144
|
+
|
|
145
|
+
<% when :color %>
|
|
146
|
+
<div class="flex items-center space-x-3">
|
|
147
|
+
<%= color_field_tag 'value', field.display_value || "#000000",
|
|
148
|
+
class: "w-20 h-10 border border-gray-300 rounded cursor-pointer" %>
|
|
149
|
+
<%= text_field_tag 'value', field.display_value || "#000000",
|
|
150
|
+
class: "flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
151
|
+
placeholder: "#000000" %>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<% when :dropdown %>
|
|
155
|
+
<% options = field.options.is_a?(Hash) ? field.options : {} %>
|
|
156
|
+
<%= select_tag 'value',
|
|
157
|
+
options_for_select(options, field.display_value),
|
|
158
|
+
class: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" %>
|
|
159
|
+
|
|
160
|
+
<% end %>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div class="flex justify-end gap-3 mt-6 pt-4 border-t border-gray-200">
|
|
164
|
+
<button type="button"
|
|
165
|
+
data-action="click->field-editor-modal#close"
|
|
166
|
+
class="cursor-pointer px-6 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50">
|
|
167
|
+
Cancel
|
|
168
|
+
</button>
|
|
169
|
+
<%= f.submit 'Save Changes',
|
|
170
|
+
class: 'cursor-pointer px-6 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700' %>
|
|
171
|
+
</div>
|
|
172
|
+
<% end %>
|
|
173
|
+
</div>
|
|
174
|
+
|