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,31 @@
|
|
|
1
|
+
class CreateLeanCmsAuthTables < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
unless table_exists?(:lean_cms_sessions)
|
|
4
|
+
create_table :lean_cms_sessions do |t|
|
|
5
|
+
t.references :user, null: false
|
|
6
|
+
t.string :ip_address
|
|
7
|
+
t.string :user_agent
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
unless table_exists?(:lean_cms_magic_links)
|
|
14
|
+
create_table :lean_cms_magic_links do |t|
|
|
15
|
+
t.references :user, null: false
|
|
16
|
+
t.string :token, null: false
|
|
17
|
+
t.string :purpose, null: false
|
|
18
|
+
t.datetime :expires_at, null: false
|
|
19
|
+
t.datetime :used_at
|
|
20
|
+
t.string :created_by_ip
|
|
21
|
+
t.string :used_from_ip
|
|
22
|
+
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
add_index :lean_cms_magic_links, :token, unique: true
|
|
27
|
+
add_index :lean_cms_magic_links, [:user_id, :purpose]
|
|
28
|
+
add_index :lean_cms_magic_links, :expires_at
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreatePaperTrailVersions < ActiveRecord::Migration[7.1]
|
|
2
|
+
# Idempotent on existing installs that already ran `paper_trail:install`.
|
|
3
|
+
def change
|
|
4
|
+
create_table :versions, if_not_exists: true do |t|
|
|
5
|
+
t.string :item_type, null: false
|
|
6
|
+
t.bigint :item_id, null: false
|
|
7
|
+
t.string :event, null: false
|
|
8
|
+
t.string :whodunnit
|
|
9
|
+
t.text :object
|
|
10
|
+
t.datetime :created_at
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
return if index_exists?(:versions, [:item_type, :item_id])
|
|
14
|
+
add_index :versions, [:item_type, :item_id]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class CreateActionTextTables < ActiveRecord::Migration[7.1]
|
|
2
|
+
# Mirrors `bin/rails action_text:install` output. Idempotent: hosts that
|
|
3
|
+
# already ran action_text:install separately are unaffected.
|
|
4
|
+
def change
|
|
5
|
+
create_table :action_text_rich_texts, if_not_exists: true do |t|
|
|
6
|
+
t.string :name, null: false
|
|
7
|
+
t.text :body
|
|
8
|
+
t.references :record, null: false, polymorphic: true, index: false
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
return if index_exists?(:action_text_rich_texts, [:record_type, :record_id, :name], name: "index_action_text_rich_texts_uniqueness")
|
|
13
|
+
add_index :action_text_rich_texts,
|
|
14
|
+
[:record_type, :record_id, :name],
|
|
15
|
+
name: "index_action_text_rich_texts_uniqueness",
|
|
16
|
+
unique: true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
class CreateActiveStorageTables < ActiveRecord::Migration[7.1]
|
|
2
|
+
# Mirrors `bin/rails active_storage:install` output. Idempotent.
|
|
3
|
+
def change
|
|
4
|
+
create_table :active_storage_blobs, if_not_exists: true do |t|
|
|
5
|
+
t.string :key, null: false
|
|
6
|
+
t.string :filename, null: false
|
|
7
|
+
t.string :content_type
|
|
8
|
+
t.text :metadata
|
|
9
|
+
t.string :service_name, null: false
|
|
10
|
+
t.bigint :byte_size, null: false
|
|
11
|
+
t.string :checksum
|
|
12
|
+
t.datetime :created_at, null: false
|
|
13
|
+
end
|
|
14
|
+
add_index :active_storage_blobs, :key, unique: true, if_not_exists: true
|
|
15
|
+
|
|
16
|
+
create_table :active_storage_attachments, if_not_exists: true do |t|
|
|
17
|
+
t.string :name, null: false
|
|
18
|
+
t.references :record, null: false, polymorphic: true, index: false
|
|
19
|
+
t.references :blob, null: false
|
|
20
|
+
t.datetime :created_at, null: false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
unless index_exists?(:active_storage_attachments,
|
|
24
|
+
[:record_type, :record_id, :name, :blob_id],
|
|
25
|
+
name: "index_active_storage_attachments_uniqueness")
|
|
26
|
+
add_index :active_storage_attachments,
|
|
27
|
+
[:record_type, :record_id, :name, :blob_id],
|
|
28
|
+
name: "index_active_storage_attachments_uniqueness",
|
|
29
|
+
unique: true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
create_table :active_storage_variant_records, if_not_exists: true do |t|
|
|
33
|
+
t.belongs_to :blob, null: false, index: false
|
|
34
|
+
t.string :variation_digest, null: false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
return if index_exists?(:active_storage_variant_records,
|
|
38
|
+
[:blob_id, :variation_digest],
|
|
39
|
+
name: "index_active_storage_variant_records_uniqueness")
|
|
40
|
+
add_index :active_storage_variant_records,
|
|
41
|
+
[:blob_id, :variation_digest],
|
|
42
|
+
name: "index_active_storage_variant_records_uniqueness",
|
|
43
|
+
unique: true
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class CreateNoticedTables < ActiveRecord::Migration[7.1]
|
|
2
|
+
# Mirrors `bin/rails noticed:install:migrations` output for noticed 3.x.
|
|
3
|
+
# Idempotent — hosts that ran `noticed:install:migrations` separately get
|
|
4
|
+
# no-ops on these tables.
|
|
5
|
+
def change
|
|
6
|
+
create_table :noticed_events, if_not_exists: true do |t|
|
|
7
|
+
t.string :type
|
|
8
|
+
t.belongs_to :record, polymorphic: true
|
|
9
|
+
if t.respond_to?(:jsonb)
|
|
10
|
+
t.jsonb :params
|
|
11
|
+
else
|
|
12
|
+
t.json :params
|
|
13
|
+
end
|
|
14
|
+
t.integer :notifications_count
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
create_table :noticed_notifications, if_not_exists: true do |t|
|
|
19
|
+
t.string :type
|
|
20
|
+
t.belongs_to :event, null: false
|
|
21
|
+
t.belongs_to :recipient, polymorphic: true, null: false
|
|
22
|
+
t.datetime :read_at
|
|
23
|
+
t.datetime :seen_at
|
|
24
|
+
t.timestamps
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module LeanCms
|
|
4
|
+
module Generators
|
|
5
|
+
class DemoGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Installs Lean CMS demo pages: Home, About, and Contact with all 9 content types"
|
|
9
|
+
|
|
10
|
+
def copy_pages_controller
|
|
11
|
+
template "pages_controller.rb", "app/controllers/pages_controller.rb"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def copy_views
|
|
15
|
+
directory "views/pages", "app/views/pages"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def copy_structure_yaml
|
|
19
|
+
template "lean_cms_structure.yml", "config/lean_cms_structure.yml",
|
|
20
|
+
force: options[:force]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_routes
|
|
24
|
+
route <<~RUBY
|
|
25
|
+
get 'about', to: 'pages#about', as: :about
|
|
26
|
+
get 'contact', to: 'pages#contact', as: :contact
|
|
27
|
+
post 'contact', to: 'pages#submit_contact'
|
|
28
|
+
root 'pages#home'
|
|
29
|
+
RUBY
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def seed_content
|
|
33
|
+
say "Seeding demo content...", :yellow
|
|
34
|
+
rake "lean_cms:load_structure"
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
say " Could not auto-seed (run 'rails lean_cms:load_structure' manually): #{e.message}", :yellow
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def print_instructions
|
|
40
|
+
say "\n#{"=" * 60}", :green
|
|
41
|
+
say "Lean CMS demo pages installed!", :green
|
|
42
|
+
say "=" * 60, :green
|
|
43
|
+
say ""
|
|
44
|
+
say "Demo pages:"
|
|
45
|
+
say " / -> Home (hero, features cards, intro text)"
|
|
46
|
+
say " /about -> About (rich text, image, bullets, boolean toggle)"
|
|
47
|
+
say " /contact -> Contact (URL, color, dropdown, form)"
|
|
48
|
+
say ""
|
|
49
|
+
say "Log in at /lean-cms to see in-context editing in action."
|
|
50
|
+
say ""
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
pages:
|
|
2
|
+
home:
|
|
3
|
+
display_title: "Home"
|
|
4
|
+
page_order: 1
|
|
5
|
+
sections:
|
|
6
|
+
hero:
|
|
7
|
+
display_title: "Hero"
|
|
8
|
+
section_order: 1
|
|
9
|
+
fields:
|
|
10
|
+
heading:
|
|
11
|
+
label: "Headline"
|
|
12
|
+
type: text
|
|
13
|
+
default: "Build Faster, Ship Smarter"
|
|
14
|
+
subheading:
|
|
15
|
+
label: "Sub-headline"
|
|
16
|
+
type: text
|
|
17
|
+
default: "Lean CMS gives Rails developers in-context editing without the bloat."
|
|
18
|
+
body:
|
|
19
|
+
label: "Body Text"
|
|
20
|
+
type: rich_text
|
|
21
|
+
default: "<p>Everything your marketing site needs, nothing it doesn't.</p>"
|
|
22
|
+
cta_text:
|
|
23
|
+
label: "CTA Button Text"
|
|
24
|
+
type: text
|
|
25
|
+
default: "Get Started"
|
|
26
|
+
cta_url:
|
|
27
|
+
label: "CTA Button URL"
|
|
28
|
+
type: url
|
|
29
|
+
default: "/contact"
|
|
30
|
+
features:
|
|
31
|
+
display_title: "Features"
|
|
32
|
+
section_order: 2
|
|
33
|
+
fields:
|
|
34
|
+
heading:
|
|
35
|
+
label: "Section Heading"
|
|
36
|
+
type: text
|
|
37
|
+
default: "Why Developers Love Lean CMS"
|
|
38
|
+
cards:
|
|
39
|
+
max_cards: 6
|
|
40
|
+
type: feature
|
|
41
|
+
items:
|
|
42
|
+
- title: "In-Context Editing"
|
|
43
|
+
body: "Hover any section to reveal an edit button. Edit content without leaving the page."
|
|
44
|
+
icon: "checkmark-badge"
|
|
45
|
+
- title: "9 Content Types"
|
|
46
|
+
body: "Text, rich text, image, boolean, URL, color, dropdown, cards, and bullets."
|
|
47
|
+
icon: "sliders"
|
|
48
|
+
- title: "SQLite First"
|
|
49
|
+
body: "Optimized for SQLite in production. Simple deployments, no external database."
|
|
50
|
+
icon: "lightning-bolt"
|
|
51
|
+
- title: "Kamal Ready"
|
|
52
|
+
body: "Deploy with Kamal. Pre-built hooks keep content safe during deploys."
|
|
53
|
+
icon: "globe"
|
|
54
|
+
|
|
55
|
+
about:
|
|
56
|
+
display_title: "About"
|
|
57
|
+
page_order: 2
|
|
58
|
+
sections:
|
|
59
|
+
intro:
|
|
60
|
+
display_title: "Introduction"
|
|
61
|
+
section_order: 1
|
|
62
|
+
fields:
|
|
63
|
+
heading:
|
|
64
|
+
label: "Page Heading"
|
|
65
|
+
type: text
|
|
66
|
+
default: "About Lean CMS"
|
|
67
|
+
body:
|
|
68
|
+
label: "Body"
|
|
69
|
+
type: rich_text
|
|
70
|
+
default: "<p>Lean CMS is a Rails Engine built for developers who want a fast, maintainable CMS without an external SaaS dependency.</p>"
|
|
71
|
+
photo:
|
|
72
|
+
label: "Team Photo"
|
|
73
|
+
type: image
|
|
74
|
+
show_badge:
|
|
75
|
+
label: "Show Trust Badge"
|
|
76
|
+
type: boolean
|
|
77
|
+
default: true
|
|
78
|
+
badge_text:
|
|
79
|
+
label: "Badge Text"
|
|
80
|
+
type: text
|
|
81
|
+
default: "Open Source since 2025"
|
|
82
|
+
values:
|
|
83
|
+
display_title: "Our Values"
|
|
84
|
+
section_order: 2
|
|
85
|
+
fields:
|
|
86
|
+
heading:
|
|
87
|
+
label: "Section Heading"
|
|
88
|
+
type: text
|
|
89
|
+
default: "What We Stand For"
|
|
90
|
+
bullets:
|
|
91
|
+
max_items: 6
|
|
92
|
+
items:
|
|
93
|
+
- "Lean over bloated — add only what you need"
|
|
94
|
+
- "Developer experience first"
|
|
95
|
+
- "Content should be versioned and auditable"
|
|
96
|
+
- "Deployment should be boring"
|
|
97
|
+
|
|
98
|
+
contact:
|
|
99
|
+
display_title: "Contact"
|
|
100
|
+
page_order: 3
|
|
101
|
+
sections:
|
|
102
|
+
info:
|
|
103
|
+
display_title: "Contact Info"
|
|
104
|
+
section_order: 1
|
|
105
|
+
fields:
|
|
106
|
+
heading:
|
|
107
|
+
label: "Page Heading"
|
|
108
|
+
type: text
|
|
109
|
+
default: "Get In Touch"
|
|
110
|
+
body:
|
|
111
|
+
label: "Intro Text"
|
|
112
|
+
type: rich_text
|
|
113
|
+
default: "<p>Have a question or want to work together? Send us a message and we'll get back to you shortly.</p>"
|
|
114
|
+
map_url:
|
|
115
|
+
label: "Google Maps URL"
|
|
116
|
+
type: url
|
|
117
|
+
default: "https://maps.google.com"
|
|
118
|
+
accent_color:
|
|
119
|
+
label: "Accent Color"
|
|
120
|
+
type: color
|
|
121
|
+
default: "#2563eb"
|
|
122
|
+
preferred_contact:
|
|
123
|
+
label: "Preferred Contact Method"
|
|
124
|
+
type: dropdown
|
|
125
|
+
options:
|
|
126
|
+
- "Email"
|
|
127
|
+
- "Phone"
|
|
128
|
+
- "Live Chat"
|
|
129
|
+
default: "Email"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class PagesController < ApplicationController
|
|
2
|
+
# LeanCms::Authentication protects every controller by default. Opt the
|
|
3
|
+
# public-facing demo pages out so non-admins can actually see them.
|
|
4
|
+
allow_unauthenticated_access
|
|
5
|
+
|
|
6
|
+
def home
|
|
7
|
+
@page = LeanCms::Page.find_by(slug: 'home')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def about
|
|
11
|
+
@page = LeanCms::Page.find_by(slug: 'about')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def contact
|
|
15
|
+
@page = LeanCms::Page.find_by(slug: 'contact')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def submit_contact
|
|
19
|
+
LeanCms::FormSubmission.create!(
|
|
20
|
+
name: params[:name],
|
|
21
|
+
email: params[:email],
|
|
22
|
+
message: params[:message]
|
|
23
|
+
)
|
|
24
|
+
redirect_to contact_path, notice: "Message sent!"
|
|
25
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
26
|
+
flash.now[:alert] = e.message
|
|
27
|
+
@page = LeanCms::Page.find_by(slug: 'contact')
|
|
28
|
+
render :contact, status: :unprocessable_entity
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<%# Demo About page — showcases rich_text, image, boolean, and bullets content types %>
|
|
2
|
+
|
|
3
|
+
<%= cms_editable_section(page: 'about', section: 'intro', display_title: 'Introduction') do %>
|
|
4
|
+
<section class="bg-white py-20">
|
|
5
|
+
<div class="max-w-4xl mx-auto px-4 grid md:grid-cols-2 gap-12 items-center">
|
|
6
|
+
<div>
|
|
7
|
+
<h1 class="text-4xl font-bold text-gray-900 mb-6">
|
|
8
|
+
<%= page_content(@page, 'intro', 'heading', default: 'About Us') %>
|
|
9
|
+
</h1>
|
|
10
|
+
<div class="prose text-gray-700">
|
|
11
|
+
<%= page_content_html(@page, 'intro', 'body') %>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<%# boolean content type: show/hide a badge based on a toggle %>
|
|
15
|
+
<% if page_content?(@page, 'intro', 'show_badge') %>
|
|
16
|
+
<div class="mt-6 inline-flex items-center px-4 py-2 bg-blue-100 text-blue-800 rounded-full text-sm font-medium">
|
|
17
|
+
<%= page_content(@page, 'intro', 'badge_text', default: 'Trusted Since 2020') %>
|
|
18
|
+
</div>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<%# image content type %>
|
|
23
|
+
<% img = page_content_image_url(@page, 'intro', 'photo') %>
|
|
24
|
+
<% if img.present? %>
|
|
25
|
+
<%= image_tag img, alt: "About us", class: "rounded-xl shadow-lg w-full object-cover aspect-square" %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
28
|
+
</section>
|
|
29
|
+
<% end %>
|
|
30
|
+
|
|
31
|
+
<%= cms_editable_section(page: 'about', section: 'values', display_title: 'Our Values') do %>
|
|
32
|
+
<section class="bg-gray-50 py-16">
|
|
33
|
+
<div class="max-w-4xl mx-auto px-4">
|
|
34
|
+
<h2 class="text-3xl font-bold text-gray-900 mb-10">
|
|
35
|
+
<%= page_content(@page, 'values', 'heading', default: 'What We Stand For') %>
|
|
36
|
+
</h2>
|
|
37
|
+
<%= bullets_section('values') %>
|
|
38
|
+
</div>
|
|
39
|
+
</section>
|
|
40
|
+
<% end %>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<%# Demo Contact page — showcases url, color, dropdown, and form submission %>
|
|
2
|
+
|
|
3
|
+
<%= cms_editable_section(page: 'contact', section: 'info', display_title: 'Contact Info') do %>
|
|
4
|
+
<section class="bg-white py-20">
|
|
5
|
+
<div class="max-w-5xl mx-auto px-4 grid md:grid-cols-2 gap-16">
|
|
6
|
+
<!-- Contact details -->
|
|
7
|
+
<div>
|
|
8
|
+
<h1 class="text-4xl font-bold text-gray-900 mb-6">
|
|
9
|
+
<%= page_content(@page, 'info', 'heading', default: 'Get In Touch') %>
|
|
10
|
+
</h1>
|
|
11
|
+
<div class="prose text-gray-700 mb-8">
|
|
12
|
+
<%= page_content_html(@page, 'info', 'body') %>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<%# url content type %>
|
|
16
|
+
<% map_url = page_content(@page, 'info', 'map_url') %>
|
|
17
|
+
<% if map_url.present? %>
|
|
18
|
+
<%= link_to "View on Google Maps", map_url, target: "_blank", class: "text-blue-600 hover:underline" %>
|
|
19
|
+
<% end %>
|
|
20
|
+
|
|
21
|
+
<%# color content type — used as an accent for the contact card border %>
|
|
22
|
+
<% accent = page_content(@page, 'info', 'accent_color', default: '#2563eb') %>
|
|
23
|
+
<div class="mt-8 p-6 rounded-xl border-l-4" style="border-color: <%= accent %>;">
|
|
24
|
+
<%# dropdown content type — e.g. preferred contact method %>
|
|
25
|
+
<p class="text-sm text-gray-500 mb-1">Preferred contact method</p>
|
|
26
|
+
<p class="font-semibold text-gray-800">
|
|
27
|
+
<%= page_content(@page, 'info', 'preferred_contact', default: 'Email') %>
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- Contact form -->
|
|
33
|
+
<div>
|
|
34
|
+
<% if flash[:notice] %>
|
|
35
|
+
<p class="mb-4 text-green-700 bg-green-100 px-4 py-3 rounded-lg"><%= flash[:notice] %></p>
|
|
36
|
+
<% end %>
|
|
37
|
+
<%= form_with url: contact_path, method: :post, class: "space-y-4" do |f| %>
|
|
38
|
+
<div>
|
|
39
|
+
<%= f.label :name, "Your Name", class: "block text-sm font-medium text-gray-700 mb-1" %>
|
|
40
|
+
<%= f.text_field :name, required: true, class: "w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" %>
|
|
41
|
+
</div>
|
|
42
|
+
<div>
|
|
43
|
+
<%= f.label :email, "Email Address", class: "block text-sm font-medium text-gray-700 mb-1" %>
|
|
44
|
+
<%= f.email_field :email, required: true, class: "w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" %>
|
|
45
|
+
</div>
|
|
46
|
+
<div>
|
|
47
|
+
<%= f.label :message, "Message", class: "block text-sm font-medium text-gray-700 mb-1" %>
|
|
48
|
+
<%= f.text_area :message, required: true, rows: 5, class: "w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" %>
|
|
49
|
+
</div>
|
|
50
|
+
<%= f.submit "Send Message", class: "w-full px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition cursor-pointer" %>
|
|
51
|
+
<% end %>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</section>
|
|
55
|
+
<% end %>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<%# Demo Home page — showcases text, rich_text, url, and cards content types %>
|
|
2
|
+
|
|
3
|
+
<%= cms_editable_section(page: 'home', section: 'hero', display_title: 'Hero') do %>
|
|
4
|
+
<section class="bg-white py-20 text-center">
|
|
5
|
+
<div class="max-w-3xl mx-auto px-4">
|
|
6
|
+
<h1 class="text-4xl font-bold text-gray-900 mb-4">
|
|
7
|
+
<%= page_content(@page, 'hero', 'heading', default: 'Welcome') %>
|
|
8
|
+
</h1>
|
|
9
|
+
<p class="text-xl text-gray-600 mb-4">
|
|
10
|
+
<%= page_content(@page, 'hero', 'subheading') %>
|
|
11
|
+
</p>
|
|
12
|
+
<div class="prose mx-auto text-gray-700 mb-8">
|
|
13
|
+
<%= page_content_html(@page, 'hero', 'body') %>
|
|
14
|
+
</div>
|
|
15
|
+
<% cta_url = page_content(@page, 'hero', 'cta_url', default: '#') %>
|
|
16
|
+
<% cta_text = page_content(@page, 'hero', 'cta_text', default: 'Get Started') %>
|
|
17
|
+
<%= link_to cta_text, cta_url, class: "inline-block px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition" %>
|
|
18
|
+
</div>
|
|
19
|
+
</section>
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
<%= cms_editable_section(page: 'home', section: 'features', display_title: 'Features') do %>
|
|
23
|
+
<section class="bg-gray-50 py-16">
|
|
24
|
+
<div class="max-w-5xl mx-auto px-4">
|
|
25
|
+
<h2 class="text-3xl font-bold text-gray-900 text-center mb-12">
|
|
26
|
+
<%= page_content(@page, 'features', 'heading', default: 'Why Choose Us') %>
|
|
27
|
+
</h2>
|
|
28
|
+
<%= cards_section('features') %>
|
|
29
|
+
</div>
|
|
30
|
+
</section>
|
|
31
|
+
<% end %>
|