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,428 @@
|
|
|
1
|
+
<% content_for :title, "Edit #{@section.humanize}" %>
|
|
2
|
+
|
|
3
|
+
<!-- Breadcrumb -->
|
|
4
|
+
<nav class="mb-6">
|
|
5
|
+
<ol class="flex items-center space-x-2 text-sm text-gray-600">
|
|
6
|
+
<li>
|
|
7
|
+
<%= link_to "Page Content", lean_cms_page_contents_path, class: "hover:text-gray-900" %>
|
|
8
|
+
</li>
|
|
9
|
+
<li>
|
|
10
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
11
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
|
|
12
|
+
</svg>
|
|
13
|
+
</li>
|
|
14
|
+
<li class="font-medium text-gray-900 capitalize"><%= @page.titleize %></li>
|
|
15
|
+
<li>
|
|
16
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
17
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
|
|
18
|
+
</svg>
|
|
19
|
+
</li>
|
|
20
|
+
<li class="font-medium text-gray-900 capitalize"><%= @section.humanize %></li>
|
|
21
|
+
</ol>
|
|
22
|
+
</nav>
|
|
23
|
+
|
|
24
|
+
<!-- Header -->
|
|
25
|
+
<div class="mb-8">
|
|
26
|
+
<h1 class="text-3xl font-bold text-gray-900 mb-2">Edit <%= @section.humanize %></h1>
|
|
27
|
+
<p class="text-gray-600">
|
|
28
|
+
Update content for the <span class="font-medium"><%= @page.titleize %></span> page
|
|
29
|
+
</p>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- Settings Override Notice for contact/info and contact/hours -->
|
|
33
|
+
<% if @page == 'contact' && ['info', 'hours'].include?(@section) %>
|
|
34
|
+
<% settings_key = @section == 'info' ? 'contact_info_override' : 'contact_hours_override' %>
|
|
35
|
+
<% override_enabled = LeanCms::Setting.get(settings_key, 'false') == 'true' %>
|
|
36
|
+
|
|
37
|
+
<div data-controller="settings-override"
|
|
38
|
+
data-settings-override-section-value="<%= @section %>">
|
|
39
|
+
<div class="bg-amber-50 border border-amber-200 rounded-lg p-4 mb-6">
|
|
40
|
+
<div class="flex items-start gap-3">
|
|
41
|
+
<svg class="w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
42
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
43
|
+
</svg>
|
|
44
|
+
<div class="flex-1">
|
|
45
|
+
<h3 class="font-medium text-amber-800">Site-Wide Settings</h3>
|
|
46
|
+
<p class="text-sm text-amber-700 mt-1">
|
|
47
|
+
This information is managed in
|
|
48
|
+
<%= link_to "Site Settings", lean_cms_settings_path(anchor: @section == 'info' ? 'site-info' : 'business-hours'),
|
|
49
|
+
class: "underline font-medium", target: "_blank" %>
|
|
50
|
+
and used across the site (contact page, footer, schema markup).
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
<div class="mt-3 flex items-center">
|
|
54
|
+
<%= form_with url: lean_cms_page_content_path(page: @page, section: @section), method: :patch, data: { turbo: false } do %>
|
|
55
|
+
<label class="flex items-center cursor-pointer">
|
|
56
|
+
<%= check_box_tag settings_key, '1', override_enabled,
|
|
57
|
+
class: "w-4 h-4 text-amber-600 bg-gray-100 border-gray-300 rounded focus:ring-amber-500 focus:ring-2",
|
|
58
|
+
data: { action: "change->settings-override#toggle", settings_override_target: "checkbox" } %>
|
|
59
|
+
<span class="ml-2 text-sm font-medium text-amber-800">Override with page-specific values</span>
|
|
60
|
+
</label>
|
|
61
|
+
<% end %>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<% unless override_enabled %>
|
|
65
|
+
<div class="mt-4 pt-3 border-t border-amber-200">
|
|
66
|
+
<p class="text-xs font-medium text-amber-700 mb-2">Current values from Settings:</p>
|
|
67
|
+
<% if @section == 'info' %>
|
|
68
|
+
<div class="text-sm text-amber-800 space-y-1">
|
|
69
|
+
<p><span class="font-medium">Address:</span> <%= LeanCms::Setting.site_address_single_line.presence || 'Not set' %></p>
|
|
70
|
+
<p><span class="font-medium">Phone:</span> <%= LeanCms::Setting.site_phone.presence || 'Not set' %></p>
|
|
71
|
+
<p><span class="font-medium">Email:</span> <%= LeanCms::Setting.site_email.presence || 'Not set' %></p>
|
|
72
|
+
</div>
|
|
73
|
+
<% else %>
|
|
74
|
+
<div class="text-sm text-amber-800 space-y-1">
|
|
75
|
+
<% LeanCms::Setting.business_hours['hours'].each do |hour| %>
|
|
76
|
+
<p><span class="font-medium"><%= hour['label'] %>:</span> <%= hour['value'] %></p>
|
|
77
|
+
<% end %>
|
|
78
|
+
<% if LeanCms::Setting.business_hours['note'].present? %>
|
|
79
|
+
<p class="mt-2 text-xs italic"><%= LeanCms::Setting.business_hours['note'] %></p>
|
|
80
|
+
<% end %>
|
|
81
|
+
</div>
|
|
82
|
+
<% end %>
|
|
83
|
+
</div>
|
|
84
|
+
<% end %>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<!-- Edit Form -->
|
|
90
|
+
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden <%= 'hidden' if @page == 'contact' && ['info', 'hours'].include?(@section) && LeanCms::Setting.get(@section == 'info' ? 'contact_info_override' : 'contact_hours_override', 'false') != 'true' %>"
|
|
91
|
+
data-settings-override-target="form">
|
|
92
|
+
<% else %>
|
|
93
|
+
|
|
94
|
+
<!-- Edit Form (non-override section) -->
|
|
95
|
+
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
|
|
96
|
+
<% end %>
|
|
97
|
+
<%= form_with url: lean_cms_page_content_path(page: @page, section: @section), method: :patch, multipart: true, class: "p-6 space-y-6" do |f| %>
|
|
98
|
+
|
|
99
|
+
<% @fields.each do |field| %>
|
|
100
|
+
<div class="border-b border-gray-100 pb-6 last:border-0 last:pb-0">
|
|
101
|
+
<label class="block text-sm font-semibold text-gray-900 mb-2">
|
|
102
|
+
<%= field.label || field.key.humanize %>
|
|
103
|
+
</label>
|
|
104
|
+
|
|
105
|
+
<% case field.content_type.to_sym %>
|
|
106
|
+
<% when :text %>
|
|
107
|
+
<% if field.key == 'hours_json' %>
|
|
108
|
+
<!-- Business Hours Dynamic Editor -->
|
|
109
|
+
<%
|
|
110
|
+
# Try to get saved override data first
|
|
111
|
+
hours_data = field.display_value.is_a?(String) ? (JSON.parse(field.display_value) rescue nil) : field.display_value
|
|
112
|
+
|
|
113
|
+
# If no override data or empty, fall back to Settings data
|
|
114
|
+
if hours_data.nil? || (hours_data['hours'].blank? && hours_data['note'].blank?)
|
|
115
|
+
hours_data = LeanCms::Setting.business_hours
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
hours = hours_data['hours'] || []
|
|
119
|
+
hours = [{ 'label' => '', 'value' => '' }] if hours.empty?
|
|
120
|
+
%>
|
|
121
|
+
<div data-controller="dynamic-list" data-dynamic-list-max-value="10">
|
|
122
|
+
<div data-dynamic-list-target="list" class="space-y-3">
|
|
123
|
+
<% hours.each do |hour| %>
|
|
124
|
+
<div data-list-item class="flex gap-3 items-center">
|
|
125
|
+
<%= text_field_tag "hours_labels[]", hour['label'],
|
|
126
|
+
placeholder: "e.g., Monday - Friday",
|
|
127
|
+
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" %>
|
|
128
|
+
<%= text_field_tag "hours_values[]", hour['value'],
|
|
129
|
+
placeholder: "e.g., 8:00 AM - 5:00 PM",
|
|
130
|
+
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" %>
|
|
131
|
+
<button type="button" data-action="dynamic-list#remove"
|
|
132
|
+
class="p-2 text-gray-400 hover:text-red-500 transition-colors cursor-pointer">
|
|
133
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
134
|
+
<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"/>
|
|
135
|
+
</svg>
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
<% end %>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<button type="button" data-action="dynamic-list#add"
|
|
142
|
+
class="mt-3 inline-flex items-center text-sm font-medium text-blue-600 hover:text-blue-700 transition-colors cursor-pointer">
|
|
143
|
+
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
144
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
|
145
|
+
</svg>
|
|
146
|
+
Add Hours Row
|
|
147
|
+
</button>
|
|
148
|
+
|
|
149
|
+
<template data-dynamic-list-target="template">
|
|
150
|
+
<div data-list-item class="flex gap-3 items-center">
|
|
151
|
+
<%= text_field_tag "hours_labels[]", "",
|
|
152
|
+
placeholder: "e.g., Monday - Friday",
|
|
153
|
+
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" %>
|
|
154
|
+
<%= text_field_tag "hours_values[]", "",
|
|
155
|
+
placeholder: "e.g., 8:00 AM - 5:00 PM",
|
|
156
|
+
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" %>
|
|
157
|
+
<button type="button" data-action="dynamic-list#remove"
|
|
158
|
+
class="p-2 text-gray-400 hover:text-red-500 transition-colors cursor-pointer">
|
|
159
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
160
|
+
<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"/>
|
|
161
|
+
</svg>
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
164
|
+
</template>
|
|
165
|
+
|
|
166
|
+
<div class="mt-4 pt-4 border-t border-gray-200">
|
|
167
|
+
<%= label_tag :hours_note, "Additional Note", class: "block text-sm font-medium text-gray-700 mb-1" %>
|
|
168
|
+
<%= text_area_tag :hours_note, hours_data['note'],
|
|
169
|
+
rows: 2,
|
|
170
|
+
placeholder: "e.g., Emergency services available 24/7 for existing clients.",
|
|
171
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 outline-none resize-none" %>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
<% else %>
|
|
175
|
+
<% if field.max_length.present? && field.max_length > 0 %>
|
|
176
|
+
<div data-controller="character-counter" data-character-counter-max-value="<%= field.max_length %>">
|
|
177
|
+
<%= text_field_tag "page_contents[#{field.id}][value]", field.display_value,
|
|
178
|
+
class: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
179
|
+
maxlength: field.max_length,
|
|
180
|
+
data: { character_counter_target: "input", action: "input->character-counter#updateCounter" } %>
|
|
181
|
+
<div class="flex justify-end mt-1">
|
|
182
|
+
<span data-character-counter-target="counter" class="text-xs text-gray-500"></span>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
<% else %>
|
|
186
|
+
<%= text_field_tag "page_contents[#{field.id}][value]", field.display_value,
|
|
187
|
+
class: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" %>
|
|
188
|
+
<% end %>
|
|
189
|
+
<% end %>
|
|
190
|
+
|
|
191
|
+
<% when :rich_text %>
|
|
192
|
+
<%= rich_text_area_tag "page_contents[#{field.id}][value]", field.display_value,
|
|
193
|
+
class: "w-full min-h-[200px] border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" %>
|
|
194
|
+
|
|
195
|
+
<% when :image %>
|
|
196
|
+
<div class="space-y-3">
|
|
197
|
+
<% if field.image_file.attached? %>
|
|
198
|
+
<div class="relative inline-block">
|
|
199
|
+
<%= image_tag field.image_file, class: "max-w-xs rounded-lg border border-gray-200" %>
|
|
200
|
+
<div class="mt-2 text-sm text-gray-600">
|
|
201
|
+
Current image: <%= field.image_file.filename %>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
<% elsif field.value.present? %>
|
|
205
|
+
<div class="text-sm text-gray-600">
|
|
206
|
+
Current URL: <%= field.value %>
|
|
207
|
+
</div>
|
|
208
|
+
<% end %>
|
|
209
|
+
|
|
210
|
+
<%= file_field_tag "page_contents[#{field.id}][image_file]",
|
|
211
|
+
accept: "image/*",
|
|
212
|
+
class: "block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 focus:outline-none" %>
|
|
213
|
+
<p class="text-xs text-gray-500">Upload a new image to replace the current one</p>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<% when :boolean %>
|
|
217
|
+
<div class="flex items-center">
|
|
218
|
+
<%= check_box_tag "page_contents[#{field.id}][value]", "1", field.display_value,
|
|
219
|
+
class: "w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2" %>
|
|
220
|
+
<span class="ml-2 text-sm text-gray-600">Enable this feature</span>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<% when :url %>
|
|
224
|
+
<%= url_field_tag "page_contents[#{field.id}][value]", field.display_value,
|
|
225
|
+
class: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
226
|
+
placeholder: "https://example.com" %>
|
|
227
|
+
|
|
228
|
+
<% when :color %>
|
|
229
|
+
<div class="flex items-center space-x-3">
|
|
230
|
+
<%= color_field_tag "page_contents[#{field.id}][value]", field.display_value || "#000000",
|
|
231
|
+
class: "w-20 h-10 border border-gray-300 rounded cursor-pointer" %>
|
|
232
|
+
<%= text_field_tag "page_contents[#{field.id}][value]", field.display_value || "#000000",
|
|
233
|
+
class: "flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
234
|
+
placeholder: "#000000" %>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<% when :dropdown %>
|
|
238
|
+
<% options = field.options.is_a?(Hash) ? field.options : {} %>
|
|
239
|
+
<%= select_tag "page_contents[#{field.id}][value]",
|
|
240
|
+
options_for_select(options, field.display_value),
|
|
241
|
+
class: "w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" %>
|
|
242
|
+
|
|
243
|
+
<% when :bullets %>
|
|
244
|
+
<%
|
|
245
|
+
bullets = field.display_value.is_a?(Array) ? field.display_value : []
|
|
246
|
+
bullets = [''] if bullets.empty?
|
|
247
|
+
%>
|
|
248
|
+
<div data-controller="dynamic-list" data-dynamic-list-max-value="20" data-dynamic-list-min-value="1">
|
|
249
|
+
<div data-dynamic-list-target="list" class="space-y-2">
|
|
250
|
+
<% bullets.each do |bullet| %>
|
|
251
|
+
<div data-list-item class="flex gap-3 items-center">
|
|
252
|
+
<%= text_field_tag "bullet_items[]", bullet,
|
|
253
|
+
placeholder: "Enter a bullet point",
|
|
254
|
+
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" %>
|
|
255
|
+
<button type="button" data-action="click->dynamic-list#remove"
|
|
256
|
+
class="p-2 text-gray-400 hover:text-red-500 transition-colors cursor-pointer flex-shrink-0">
|
|
257
|
+
<svg class="w-5 h-5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
258
|
+
<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"/>
|
|
259
|
+
</svg>
|
|
260
|
+
</button>
|
|
261
|
+
</div>
|
|
262
|
+
<% end %>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<button type="button" data-action="click->dynamic-list#add"
|
|
266
|
+
class="mt-3 inline-flex items-center text-sm font-medium text-blue-600 hover:text-blue-700 transition-colors cursor-pointer">
|
|
267
|
+
<svg class="w-4 h-4 mr-1 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
268
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
|
269
|
+
</svg>
|
|
270
|
+
Add Bullet Point
|
|
271
|
+
</button>
|
|
272
|
+
|
|
273
|
+
<template data-dynamic-list-target="template">
|
|
274
|
+
<div data-list-item class="flex gap-3 items-center">
|
|
275
|
+
<%= text_field_tag "bullet_items[]", "",
|
|
276
|
+
placeholder: "Enter a bullet point",
|
|
277
|
+
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" %>
|
|
278
|
+
<button type="button" data-action="click->dynamic-list#remove"
|
|
279
|
+
class="p-2 text-gray-400 hover:text-red-500 transition-colors cursor-pointer flex-shrink-0">
|
|
280
|
+
<svg class="w-5 h-5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
281
|
+
<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"/>
|
|
282
|
+
</svg>
|
|
283
|
+
</button>
|
|
284
|
+
</div>
|
|
285
|
+
</template>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<!-- Hidden field to store the bullets field ID for controller processing -->
|
|
289
|
+
<input type="hidden" name="bullets_field_id" value="<%= field.id %>">
|
|
290
|
+
|
|
291
|
+
<% when :cards %>
|
|
292
|
+
<% cards = field.display_value.is_a?(Array) ? field.display_value : [] %>
|
|
293
|
+
<script>
|
|
294
|
+
window._cardEditorInitial_<%= field.id %> = <%= raw json_escape(cards.to_json) %>;
|
|
295
|
+
</script>
|
|
296
|
+
<div x-data="cardEditor(window['_cardEditorInitial_<%= field.id %>'])" class="space-y-4">
|
|
297
|
+
<!-- Cards List -->
|
|
298
|
+
<div class="space-y-3">
|
|
299
|
+
<template x-for="(card, index) in cards" :key="index">
|
|
300
|
+
<div class="border border-gray-200 rounded-lg p-4 bg-gray-50">
|
|
301
|
+
<div class="flex items-center justify-between mb-3">
|
|
302
|
+
<h4 class="font-semibold text-gray-900" x-text="`Card ${index + 1}`"></h4>
|
|
303
|
+
<button type="button" @click="removeCard(index)"
|
|
304
|
+
class="text-red-600 hover:text-red-800 text-sm font-medium cursor-pointer">
|
|
305
|
+
Remove
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<div class="grid grid-cols-2 gap-4">
|
|
310
|
+
<!-- Icon -->
|
|
311
|
+
<div>
|
|
312
|
+
<label class="block text-xs font-medium text-gray-700 mb-1">Icon</label>
|
|
313
|
+
<select x-model="card.icon"
|
|
314
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
315
|
+
<option value="checkmark-badge">Checkmark Badge</option>
|
|
316
|
+
<option value="checkmark-circle">Checkmark Circle</option>
|
|
317
|
+
<option value="lock">Lock</option>
|
|
318
|
+
<option value="lightning-bolt">Lightning Bolt</option>
|
|
319
|
+
<option value="users">Users</option>
|
|
320
|
+
<option value="sliders">Sliders</option>
|
|
321
|
+
<option value="globe">Globe</option>
|
|
322
|
+
<option value="support">Support</option>
|
|
323
|
+
<option value="control-panel">Control Panel</option>
|
|
324
|
+
<option value="gear">Gear</option>
|
|
325
|
+
</select>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<!-- Icon Color -->
|
|
329
|
+
<div>
|
|
330
|
+
<label class="block text-xs font-medium text-gray-700 mb-1">Icon Color</label>
|
|
331
|
+
<input type="text" x-model="card.icon_color"
|
|
332
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
333
|
+
placeholder="#000000 or white">
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<!-- Background Color -->
|
|
337
|
+
<div>
|
|
338
|
+
<label class="block text-xs font-medium text-gray-700 mb-1">Background Color</label>
|
|
339
|
+
<input type="text" x-model="card.bg_color"
|
|
340
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
341
|
+
placeholder="#ffffff or gradient-red">
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
<!-- Alignment -->
|
|
345
|
+
<div>
|
|
346
|
+
<label class="block text-xs font-medium text-gray-700 mb-1">Alignment</label>
|
|
347
|
+
<select x-model="card.alignment"
|
|
348
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
349
|
+
<option value="left">Left</option>
|
|
350
|
+
<option value="center">Center</option>
|
|
351
|
+
<option value="right">Right</option>
|
|
352
|
+
</select>
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<!-- Heading -->
|
|
356
|
+
<div class="col-span-2">
|
|
357
|
+
<label class="block text-xs font-medium text-gray-700 mb-1">Heading</label>
|
|
358
|
+
<input type="text" x-model="card.heading"
|
|
359
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
360
|
+
placeholder="Card heading">
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
<!-- Text -->
|
|
364
|
+
<div class="col-span-2">
|
|
365
|
+
<label class="block text-xs font-medium text-gray-700 mb-1">Text</label>
|
|
366
|
+
<textarea x-model="card.text" rows="2"
|
|
367
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none"
|
|
368
|
+
placeholder="Card description"></textarea>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
</template>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
<!-- Add Card Button -->
|
|
376
|
+
<button type="button" @click="addCard"
|
|
377
|
+
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">
|
|
378
|
+
+ Add Card
|
|
379
|
+
</button>
|
|
380
|
+
|
|
381
|
+
<!-- Hidden input to store JSON -->
|
|
382
|
+
<input type="hidden"
|
|
383
|
+
:name="'page_contents[<%= field.id %>][value]'"
|
|
384
|
+
:value="JSON.stringify(cards)">
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<script>
|
|
388
|
+
function cardEditor(initialCards) {
|
|
389
|
+
return {
|
|
390
|
+
cards: initialCards.length > 0 ? initialCards : [],
|
|
391
|
+
addCard() {
|
|
392
|
+
this.cards.push({
|
|
393
|
+
icon: 'checkmark-circle',
|
|
394
|
+
icon_color: '#2563eb',
|
|
395
|
+
bg_color: '#dbeafe',
|
|
396
|
+
heading: '',
|
|
397
|
+
text: '',
|
|
398
|
+
alignment: 'left'
|
|
399
|
+
});
|
|
400
|
+
},
|
|
401
|
+
removeCard(index) {
|
|
402
|
+
this.cards.splice(index, 1);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
</script>
|
|
407
|
+
|
|
408
|
+
<% end %>
|
|
409
|
+
|
|
410
|
+
<% if field.key.present? && !%w[hours_json bullets].include?(field.key) %>
|
|
411
|
+
<p class="mt-1 text-xs text-gray-500">Field key: <code class="bg-gray-100 px-1 py-0.5 rounded"><%= field.key %></code></p>
|
|
412
|
+
<% end %>
|
|
413
|
+
</div>
|
|
414
|
+
<% end %>
|
|
415
|
+
|
|
416
|
+
<!-- Form Actions -->
|
|
417
|
+
<div class="flex items-center justify-between pt-6 border-t border-gray-200">
|
|
418
|
+
<%= link_to "Cancel", lean_cms_page_contents_path,
|
|
419
|
+
class: "px-6 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors" %>
|
|
420
|
+
|
|
421
|
+
<%= f.submit "Save Changes",
|
|
422
|
+
class: "px-6 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors" %>
|
|
423
|
+
</div>
|
|
424
|
+
<% end %>
|
|
425
|
+
</div>
|
|
426
|
+
<% if @page == 'contact' && ['info', 'hours'].include?(@section) %>
|
|
427
|
+
</div><!-- Close controller wrapper -->
|
|
428
|
+
<% end %>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<% content_for :title, "Page Content" %>
|
|
2
|
+
|
|
3
|
+
<div class="mb-8">
|
|
4
|
+
<h1 class="text-3xl font-bold text-gray-900 mb-2">Page Content</h1>
|
|
5
|
+
<p class="text-gray-600">Manage editable content across all pages</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<% if @pages.empty? %>
|
|
9
|
+
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
|
|
10
|
+
<div class="flex items-start">
|
|
11
|
+
<svg class="w-6 h-6 text-yellow-600 mt-1 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
|
13
|
+
</svg>
|
|
14
|
+
<div>
|
|
15
|
+
<h3 class="text-lg font-semibold text-yellow-800 mb-1">No Page Content Found</h3>
|
|
16
|
+
<p class="text-yellow-700">Run <code class="bg-yellow-100 px-2 py-1 rounded">rake lean_cms:load_structure</code> to load page content from your YAML structure file.</p>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<% else %>
|
|
21
|
+
<div class="space-y-3" x-data="{ openPage: '<%= @pages.first[:key] %>' }">
|
|
22
|
+
<% @pages.each do |page_data| %>
|
|
23
|
+
<% page = page_data[:key] %>
|
|
24
|
+
<% page_title = page_data[:display_title] %>
|
|
25
|
+
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
|
|
26
|
+
<!-- Page Header (Collapsible) -->
|
|
27
|
+
<button
|
|
28
|
+
@click="openPage = openPage === '<%= page %>' ? '' : '<%= page %>'"
|
|
29
|
+
class="w-full bg-gradient-to-r from-gray-50 to-white px-6 py-4 border-b border-gray-200 hover:from-gray-100 hover:to-gray-50 transition-colors cursor-pointer">
|
|
30
|
+
<div class="flex items-center justify-between">
|
|
31
|
+
<div class="flex items-center gap-3">
|
|
32
|
+
<!-- Chevron Icon -->
|
|
33
|
+
<svg class="w-5 h-5 text-gray-400 transition-transform"
|
|
34
|
+
:class="openPage === '<%= page %>' ? 'rotate-90' : ''"
|
|
35
|
+
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
37
|
+
</svg>
|
|
38
|
+
|
|
39
|
+
<div class="text-left">
|
|
40
|
+
<h2 class="text-xl font-bold text-gray-900"><%= page_title %></h2>
|
|
41
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
42
|
+
<%= @page_structure[page].length %> sections •
|
|
43
|
+
<%= @page_structure[page].sum { |s| s[:field_count] } %> fields
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<%= link_to "/#{page}", target: "_blank",
|
|
49
|
+
class: "text-sm text-gray-600 hover:text-gray-900 flex items-center gap-1 cursor-pointer",
|
|
50
|
+
onclick: "event.stopPropagation();" do %>
|
|
51
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
52
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
|
|
53
|
+
</svg>
|
|
54
|
+
View Page
|
|
55
|
+
<% end %>
|
|
56
|
+
</div>
|
|
57
|
+
</button>
|
|
58
|
+
|
|
59
|
+
<!-- Sections List (Collapsible Content) -->
|
|
60
|
+
<div x-show="openPage === '<%= page %>'"
|
|
61
|
+
x-collapse
|
|
62
|
+
class="divide-y divide-gray-100">
|
|
63
|
+
<% @page_structure[page].each do |section_data| %>
|
|
64
|
+
<div class="px-6 py-4 hover:bg-gray-50 transition-colors">
|
|
65
|
+
<div class="flex items-center justify-between">
|
|
66
|
+
<div class="flex-1">
|
|
67
|
+
<div class="flex items-center gap-2">
|
|
68
|
+
<h3 class="text-base font-semibold text-gray-900">
|
|
69
|
+
<%= section_data[:display_title] %>
|
|
70
|
+
</h3>
|
|
71
|
+
<% if section_data[:has_cards] %>
|
|
72
|
+
<span class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-blue-100 text-blue-800">
|
|
73
|
+
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
74
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/>
|
|
75
|
+
</svg>
|
|
76
|
+
Cards
|
|
77
|
+
</span>
|
|
78
|
+
<% end %>
|
|
79
|
+
<% if section_data[:has_bullets] %>
|
|
80
|
+
<span class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-green-100 text-green-800">
|
|
81
|
+
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
82
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16"/>
|
|
83
|
+
</svg>
|
|
84
|
+
Bullets
|
|
85
|
+
</span>
|
|
86
|
+
<% end %>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="flex items-center gap-4 mt-1">
|
|
89
|
+
<p class="text-sm text-gray-600">
|
|
90
|
+
<%= section_data[:field_count] %> <%= 'field'.pluralize(section_data[:field_count]) %>
|
|
91
|
+
</p>
|
|
92
|
+
<% if section_data[:last_updated] %>
|
|
93
|
+
<p class="text-sm text-gray-500">
|
|
94
|
+
Updated <%= time_ago_in_words(section_data[:last_updated]) %> ago
|
|
95
|
+
</p>
|
|
96
|
+
<% end %>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<%= link_to lean_cms_edit_page_content_path(page: page, section: section_data[:section]),
|
|
100
|
+
class: "flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-colors hover:bg-gray-100 cursor-pointer" do %>
|
|
101
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
102
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
|
103
|
+
</svg>
|
|
104
|
+
Edit
|
|
105
|
+
<% end %>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
<% end %>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<% end %>
|
|
112
|
+
</div>
|
|
113
|
+
<% end %>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<% content_for :title, @magic_link.invitation? ? "Set Up Your Account" : "Reset Your Password" %>
|
|
2
|
+
|
|
3
|
+
<h1 class="lc-auth-title">
|
|
4
|
+
<%= @magic_link.invitation? ? "Set up your account" : "Reset your password" %>
|
|
5
|
+
</h1>
|
|
6
|
+
|
|
7
|
+
<p class="lc-auth-lede">
|
|
8
|
+
<% if @magic_link.invitation? %>
|
|
9
|
+
Welcome, <%= @user.display_name %>! Choose a password to activate your account.
|
|
10
|
+
<% else %>
|
|
11
|
+
Enter a new password below.
|
|
12
|
+
<% end %>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<%= form_with url: lean_cms_password_setup_path(token: @magic_link.token), method: :patch, class: "lc-auth-form" do |form| %>
|
|
16
|
+
<div class="lc-auth-field">
|
|
17
|
+
<%= form.label :password, "New password" %>
|
|
18
|
+
<%= form.password_field :password,
|
|
19
|
+
required: true, autofocus: true, autocomplete: "new-password",
|
|
20
|
+
placeholder: "Enter new password (minimum 8 characters)",
|
|
21
|
+
minlength: 8,
|
|
22
|
+
class: "lc-auth-input" %>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="lc-auth-field">
|
|
26
|
+
<%= form.label :password_confirmation, "Confirm password" %>
|
|
27
|
+
<%= form.password_field :password_confirmation,
|
|
28
|
+
required: true, autocomplete: "new-password",
|
|
29
|
+
placeholder: "Confirm your password",
|
|
30
|
+
minlength: 8,
|
|
31
|
+
class: "lc-auth-input" %>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<%= form.submit "Set password", class: "lc-auth-btn lc-auth-btn--block" %>
|
|
35
|
+
<% end %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<% content_for :title, "Reset Password" %>
|
|
2
|
+
|
|
3
|
+
<h1 class="lc-auth-title">Update your password</h1>
|
|
4
|
+
<p class="lc-auth-lede">Enter a new password below.</p>
|
|
5
|
+
|
|
6
|
+
<%= form_with url: lean_cms_password_path(params[:token]), method: :put, class: "lc-auth-form" do |form| %>
|
|
7
|
+
<div class="lc-auth-field">
|
|
8
|
+
<%= form.label :password, "New password" %>
|
|
9
|
+
<%= form.password_field :password,
|
|
10
|
+
required: true, autofocus: true, autocomplete: "new-password",
|
|
11
|
+
placeholder: "Enter new password",
|
|
12
|
+
maxlength: 72,
|
|
13
|
+
class: "lc-auth-input" %>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="lc-auth-field">
|
|
17
|
+
<%= form.label :password_confirmation, "Confirm new password" %>
|
|
18
|
+
<%= form.password_field :password_confirmation,
|
|
19
|
+
required: true, autocomplete: "new-password",
|
|
20
|
+
placeholder: "Repeat new password",
|
|
21
|
+
maxlength: 72,
|
|
22
|
+
class: "lc-auth-input" %>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<%= form.submit "Update password", class: "lc-auth-btn lc-auth-btn--block" %>
|
|
26
|
+
<% end %>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<% content_for :title, "Forgot Password" %>
|
|
2
|
+
|
|
3
|
+
<h1 class="lc-auth-title">Forgot your password?</h1>
|
|
4
|
+
<p class="lc-auth-lede">Enter your email address and we'll send you instructions to reset it.</p>
|
|
5
|
+
|
|
6
|
+
<%= form_with url: lean_cms_passwords_path, class: "lc-auth-form" do |form| %>
|
|
7
|
+
<div class="lc-auth-field">
|
|
8
|
+
<%= form.label :email_address, "Email address" %>
|
|
9
|
+
<%= form.email_field :email_address,
|
|
10
|
+
required: true, autofocus: true, autocomplete: "username",
|
|
11
|
+
placeholder: "you@example.com",
|
|
12
|
+
value: params[:email_address],
|
|
13
|
+
class: "lc-auth-input" %>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<%= form.submit "Email reset instructions", class: "lc-auth-btn lc-auth-btn--block" %>
|
|
17
|
+
|
|
18
|
+
<div class="lc-auth-actions" style="justify-content: center;">
|
|
19
|
+
<%= link_to "Back to sign in", lean_cms_new_session_path, class: "lc-auth-link" %>
|
|
20
|
+
</div>
|
|
21
|
+
<% end %>
|