ruby_cms 0.1.1 → 0.1.2
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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +68 -164
- data/app/components/ruby_cms/admin/admin_page.rb +19 -19
- data/app/components/ruby_cms/admin/admin_page_header.rb +81 -0
- data/app/components/ruby_cms/admin/admin_resource_card.rb +55 -0
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table.rb +4 -4
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_actions.rb +5 -5
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_body.rb +1 -1
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_cell.rb +15 -13
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_head.rb +13 -11
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_delete_modal.rb +9 -9
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header.rb +2 -2
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header_bar.rb +8 -8
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_pagination.rb +9 -9
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_row.rb +3 -4
- data/app/components/ruby_cms/admin/bulk_action_table/bulk_actions.rb +25 -24
- data/app/controllers/ruby_cms/admin/base_controller.rb +10 -4
- data/app/controllers/ruby_cms/admin/content_blocks_controller.rb +4 -3
- data/app/controllers/ruby_cms/admin/locale_controller.rb +2 -1
- data/app/controllers/ruby_cms/admin/user_permissions_controller.rb +25 -7
- data/app/helpers/ruby_cms/settings_helper.rb +19 -9
- data/app/javascript/controllers/ruby_cms/bulk_action_table_controller.js +53 -12
- data/app/models/ruby_cms/permission.rb +38 -9
- data/app/models/ruby_cms/permittable.rb +0 -2
- data/app/views/layouts/ruby_cms/_admin_sidebar.html.erb +2 -2
- data/app/views/layouts/ruby_cms/admin.html.erb +13 -17
- data/app/views/ruby_cms/admin/content_blocks/index.html.erb +0 -11
- data/app/views/ruby_cms/admin/content_blocks/show.html.erb +204 -85
- data/app/views/ruby_cms/admin/settings/index.html.erb +214 -175
- data/app/views/ruby_cms/admin/user_permissions/index.html.erb +32 -2
- data/app/views/ruby_cms/admin/users/_row.html.erb +4 -1
- data/config/locales/en.yml +4 -0
- data/lib/ruby_cms/engine.rb +20 -12
- data/lib/ruby_cms/version.rb +1 -1
- data/lib/ruby_cms.rb +24 -0
- metadata +4 -3
- data/app/views/ruby_cms/admin/content_blocks/edit.html.erb +0 -17
|
@@ -9,20 +9,49 @@ module RubyCms
|
|
|
9
9
|
|
|
10
10
|
validates :key, presence: true, uniqueness: true
|
|
11
11
|
|
|
12
|
-
DEFAULT_KEYS =
|
|
13
|
-
manage_admin
|
|
14
|
-
manage_permissions
|
|
15
|
-
manage_content_blocks
|
|
16
|
-
manage_visitor_errors
|
|
17
|
-
manage_analytics
|
|
18
|
-
].freeze
|
|
12
|
+
DEFAULT_KEYS = RubyCms::DEFAULT_PERMISSION_KEYS
|
|
19
13
|
|
|
20
14
|
class << self
|
|
21
15
|
def ensure_defaults!
|
|
22
|
-
|
|
23
|
-
find_or_create_by!(key: k) {|p| p.name = k.
|
|
16
|
+
all_keys.each do |k|
|
|
17
|
+
find_or_create_by!(key: k) {|p| p.name = k.titleize }
|
|
24
18
|
end
|
|
25
19
|
end
|
|
20
|
+
|
|
21
|
+
def all_keys
|
|
22
|
+
(DEFAULT_KEYS + RubyCms.extra_permission_keys.map(&:to_s)).uniq.freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def templates
|
|
26
|
+
RubyCms.permission_templates
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def register_keys(*keys)
|
|
30
|
+
RubyCms.register_permission_keys(*keys)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def register_template(name, label:, keys:, description: nil)
|
|
34
|
+
RubyCms.register_permission_template(name, label:, keys:, description:)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def apply_template!(user, template_name)
|
|
38
|
+
tmpl = templates[template_name.to_sym]
|
|
39
|
+
raise ArgumentError, "Unknown template: #{template_name}" unless tmpl
|
|
40
|
+
|
|
41
|
+
ensure_defaults!
|
|
42
|
+
perms = where(key: tmpl[:keys])
|
|
43
|
+
perms.each do |perm|
|
|
44
|
+
RubyCms::UserPermission.find_or_create_by!(user: user, permission: perm)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def matching_templates(user)
|
|
49
|
+
user_keys = RubyCms::UserPermission.where(user: user)
|
|
50
|
+
.joins(:permission)
|
|
51
|
+
.pluck("permissions.key")
|
|
52
|
+
templates.select {|_, tmpl| (tmpl[:keys] - user_keys).empty? }
|
|
53
|
+
.keys
|
|
54
|
+
end
|
|
26
55
|
end
|
|
27
56
|
end
|
|
28
57
|
end
|
|
@@ -12,9 +12,7 @@ module RubyCms
|
|
|
12
12
|
k = permission_key.to_s
|
|
13
13
|
return false unless RubyCms::Permission.exists?(key: k)
|
|
14
14
|
|
|
15
|
-
# Treat manage_admin as a super-permission for admin features.
|
|
16
15
|
cms_permission_keys_cached.include?(k) ||
|
|
17
|
-
cms_permission_keys_cached.include?("manage_admin") ||
|
|
18
16
|
record&.can_edit?(self)
|
|
19
17
|
end
|
|
20
18
|
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
|
|
77
77
|
<div class="px-3 py-3 flex-shrink-0 bg-[#FAF9F5]" data-ruby-cms--mobile-menu-target="sidebarContent">
|
|
78
78
|
<div class="mb-2">
|
|
79
|
-
<%= form_with url: ruby_cms_admin_locale_path, method: :patch, scope: nil, data: { turbo:
|
|
79
|
+
<%= form_with url: ruby_cms_admin_locale_path, method: :patch, scope: nil, data: { turbo: false } do |form| %>
|
|
80
80
|
<div class="relative">
|
|
81
81
|
<div class="flex items-center gap-3 w-full px-3 py-2.5 text-sm font-medium text-gray-600 rounded-md no-underline transition-colors hover:bg-blue-500 hover:text-white">
|
|
82
82
|
<svg class="w-5 h-5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
{
|
|
95
95
|
class: "absolute inset-0 w-full h-full opacity-0 cursor-pointer",
|
|
96
96
|
aria: { label: "Locale" },
|
|
97
|
-
onchange: "this.form.
|
|
97
|
+
onchange: "this.form.submit()"
|
|
98
98
|
} %>
|
|
99
99
|
</div>
|
|
100
100
|
<% end %>
|
|
@@ -39,34 +39,30 @@
|
|
|
39
39
|
<% else %>
|
|
40
40
|
<div class="flex-1 flex flex-col min-h-0 p-6 overflow-y-auto bg-gray-50">
|
|
41
41
|
<div class="mx-auto w-full max-w-7xl">
|
|
42
|
+
<%# Page header – prefer AdminPageHeader component rendered by views.
|
|
43
|
+
Legacy content_for fallback kept for pages that haven't migrated. %>
|
|
42
44
|
<% if content_for?(:title) || content_for?(:header_actions) %>
|
|
43
|
-
<header class="flex-shrink-0 mb-4
|
|
45
|
+
<header class="flex-shrink-0 mb-4">
|
|
46
|
+
<% if content_for?(:breadcrumbs) %>
|
|
47
|
+
<nav class="mb-1 text-sm text-muted-foreground" aria-label="Breadcrumb">
|
|
48
|
+
<ol class="flex items-center flex-wrap gap-y-1">
|
|
49
|
+
<%= yield :breadcrumbs %>
|
|
50
|
+
</ol>
|
|
51
|
+
</nav>
|
|
52
|
+
<% end %>
|
|
44
53
|
<div class="flex flex-wrap items-center justify-between gap-4">
|
|
45
|
-
<div>
|
|
54
|
+
<div class="min-w-0">
|
|
46
55
|
<% if content_for?(:title) %>
|
|
47
|
-
<h1 class="text-lg font-semibold tracking-tight text-
|
|
48
|
-
<% end %>
|
|
49
|
-
<% if content_for?(:breadcrumbs) %>
|
|
50
|
-
<nav class="mt-0.5 text-sm text-gray-500" aria-label="Breadcrumb">
|
|
51
|
-
<ol class="flex items-center flex-wrap gap-x-2 gap-y-1">
|
|
52
|
-
<%= yield :breadcrumbs %>
|
|
53
|
-
</ol>
|
|
54
|
-
</nav>
|
|
56
|
+
<h1 class="text-lg font-semibold tracking-tight text-foreground"><%= yield :title %></h1>
|
|
55
57
|
<% end %>
|
|
56
58
|
</div>
|
|
57
59
|
<% if content_for?(:header_actions) %>
|
|
58
|
-
<div class="flex items-center flex-shrink-0">
|
|
60
|
+
<div class="flex items-center gap-3 flex-shrink-0">
|
|
59
61
|
<%= yield :header_actions %>
|
|
60
62
|
</div>
|
|
61
63
|
<% end %>
|
|
62
64
|
</div>
|
|
63
65
|
</header>
|
|
64
|
-
<% elsif content_for?(:breadcrumbs) %>
|
|
65
|
-
<nav class="mb-4" aria-label="Breadcrumb">
|
|
66
|
-
<ol class="flex items-center space-x-2 text-sm text-gray-600">
|
|
67
|
-
<%= content_for :breadcrumbs %>
|
|
68
|
-
</ol>
|
|
69
|
-
</nav>
|
|
70
66
|
<% end %>
|
|
71
67
|
|
|
72
68
|
<%= render "layouts/ruby_cms/admin_flash_messages" %>
|
|
@@ -29,18 +29,7 @@
|
|
|
29
29
|
end
|
|
30
30
|
%>
|
|
31
31
|
|
|
32
|
-
<% locale_filter_url = ruby_cms_admin_content_blocks_path %>
|
|
33
|
-
<% locale_filter_html = capture do %>
|
|
34
|
-
<%= tag.div class: "flex items-center gap-1" do %>
|
|
35
|
-
<%= tag.span "Locale:", class: "text-xs font-medium text-gray-500 mr-1" %>
|
|
36
|
-
<% ([nil] + I18n.available_locales.to_a).each do |loc| %>
|
|
37
|
-
<% is_active = (params[:locale].blank? && loc.nil?) || (loc && params[:locale].to_s == loc.to_s) %>
|
|
38
|
-
<%= link_to (loc ? ruby_cms_locale_display_name(loc) : "All"), (loc ? "#{locale_filter_url}?locale=#{loc}" : locale_filter_url), class: "inline-flex items-center rounded-md px-2.5 py-1 text-xs font-medium transition-colors no-underline #{is_active ? 'bg-gray-900 text-white' : 'bg-white border border-gray-200 text-gray-700 hover:bg-gray-50'}", data: { turbo_frame: "admin_table_content" } %>
|
|
39
|
-
<% end %>
|
|
40
|
-
<% end %>
|
|
41
|
-
<% end %>
|
|
42
32
|
<%= render partial: "ruby_cms/admin/shared/bulk_action_table_index", locals: {
|
|
43
|
-
header_filter: locale_filter_html,
|
|
44
33
|
title: "Content blocks",
|
|
45
34
|
collection: @content_blocks || [],
|
|
46
35
|
headers: ["Key", "Locale", "Title", "Type", "Published", { text: "Actions", class: "text-right" }],
|
|
@@ -1,110 +1,229 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</
|
|
1
|
+
<%= render RubyCms::Admin::AdminPageHeader.new(
|
|
2
|
+
title: @content_block.key,
|
|
3
|
+
subtitle: ruby_cms_locale_display_name(@content_block.locale),
|
|
4
|
+
breadcrumbs: [
|
|
5
|
+
{ label: t("ruby_cms.admin.nav.admin", default: "Admin"), url: ruby_cms_admin_root_path },
|
|
6
|
+
{ label: t("ruby_cms.admin.nav.content_blocks", default: "Content Blocks"), url: ruby_cms_admin_content_blocks_path },
|
|
7
|
+
{ label: @content_block.key }
|
|
8
|
+
]
|
|
9
|
+
) do %>
|
|
10
|
+
<% if @content_block.published? %>
|
|
11
|
+
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full border bg-emerald-100 text-emerald-700 border-emerald-200">Published</span>
|
|
12
|
+
<% else %>
|
|
13
|
+
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full border bg-muted text-muted-foreground border-border/60">Draft</span>
|
|
14
|
+
<% end %>
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
<%= link_to ruby_cms_admin_content_block_path(@content_block),
|
|
17
|
+
data: { turbo_method: :delete, turbo_confirm: t("ruby_cms.admin.content_blocks.confirm_delete", default: "Are you sure?") },
|
|
18
|
+
class: "inline-flex items-center justify-center size-8 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors" do %>
|
|
19
|
+
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
20
|
+
<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"/>
|
|
21
|
+
</svg>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
<%= form_with model: @content_block,
|
|
26
|
+
url: ruby_cms_admin_content_block_path(@content_block),
|
|
27
|
+
method: :patch,
|
|
28
|
+
id: "content-block-form",
|
|
29
|
+
data: { turbo: false } do |f| %>
|
|
30
|
+
<div class="<%= RubyCms::Admin::AdminResourceCard::CARD_CLASS %>">
|
|
31
|
+
<div class="<%= RubyCms::Admin::AdminResourceCard::GRID_CLASS %>">
|
|
32
|
+
<%# ── Form Fields (Left 2/3) ── %>
|
|
33
|
+
<div class="<%= RubyCms::Admin::AdminResourceCard::MAIN_CLASS %>">
|
|
34
|
+
<% if @content_block.errors.any? %>
|
|
35
|
+
<div class="rounded-lg border border-destructive/30 bg-destructive/5 p-4">
|
|
36
|
+
<div class="flex gap-3">
|
|
37
|
+
<svg class="size-5 text-destructive shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
|
38
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
|
39
|
+
</svg>
|
|
40
|
+
<div>
|
|
41
|
+
<p class="text-sm font-medium text-destructive"><%= t("ruby_cms.admin.content_blocks.errors_title", default: "Please fix the following errors:") %></p>
|
|
42
|
+
<ul class="mt-1.5 text-sm text-destructive/80 list-disc list-inside">
|
|
43
|
+
<% @content_block.errors.full_messages.each do |message| %>
|
|
44
|
+
<li><%= message %></li>
|
|
45
|
+
<% end %>
|
|
46
|
+
</ul>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<% end %>
|
|
20
51
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
52
|
+
<%# ── Key & Content Type ── %>
|
|
53
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
|
54
|
+
<div>
|
|
55
|
+
<label class="<%= RubyCms::Admin::AdminResourceCard::LABEL_CLASS %>"><%= t("ruby_cms.admin.content_blocks.key", default: "Key") %></label>
|
|
56
|
+
<span class="block text-sm font-mono text-foreground py-2"><%= @content_block.key %></span>
|
|
57
|
+
<%= f.hidden_field :key, name: nil, value: @content_block.key %>
|
|
58
|
+
</div>
|
|
25
59
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
60
|
+
<div>
|
|
61
|
+
<label class="<%= RubyCms::Admin::AdminResourceCard::LABEL_CLASS %>"><%= t("ruby_cms.admin.content_blocks.content_type", default: "Content Type") %></label>
|
|
62
|
+
<%= select_tag "content_block[content_type]",
|
|
63
|
+
options_for_select(::ContentBlock::CONTENT_TYPES, @content_block.content_type),
|
|
64
|
+
class: RubyCms::Admin::AdminResourceCard::INPUT_CLASS %>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
30
67
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
68
|
+
<%# ── Published checkbox ── %>
|
|
69
|
+
<% if @blocks_by_locale.present? %>
|
|
70
|
+
<label class="inline-flex items-center gap-3 cursor-pointer">
|
|
71
|
+
<%= hidden_field_tag "content_block[published]", "0" %>
|
|
72
|
+
<%= check_box_tag "content_block[published]", "1",
|
|
73
|
+
@blocks_by_locale.values.any?(&:published?),
|
|
74
|
+
class: "size-4 rounded border-border text-primary focus:ring-primary/30 cursor-pointer" %>
|
|
75
|
+
<span class="text-sm font-medium text-foreground"><%= t("ruby_cms.admin.content_blocks.published", default: "Published") %> (all translations)</span>
|
|
76
|
+
</label>
|
|
77
|
+
<% end %>
|
|
78
|
+
|
|
79
|
+
<%# ── Locale Tabs ── %>
|
|
80
|
+
<% if @blocks_by_locale.present? %>
|
|
81
|
+
<div class="rounded-xl border border-border/60 overflow-hidden" data-controller="ruby-cms--locale-tabs">
|
|
82
|
+
<div class="flex gap-1 p-2 bg-muted/30 border-b border-border/60" role="tablist">
|
|
83
|
+
<% @blocks_by_locale.each_with_index do |(locale_s, _), idx| %>
|
|
84
|
+
<button type="button"
|
|
85
|
+
role="tab"
|
|
86
|
+
aria-selected="<%= idx == 0 %>"
|
|
87
|
+
aria-controls="locale-panel-<%= locale_s %>"
|
|
88
|
+
id="locale-tab-<%= locale_s %>"
|
|
89
|
+
data-ruby-cms--locale-tabs-target="tab"
|
|
90
|
+
data-panel-id="locale-panel-<%= locale_s %>"
|
|
91
|
+
data-action="click->ruby-cms--locale-tabs#switchTab"
|
|
92
|
+
class="px-3 py-1.5 rounded-md text-xs font-medium border-none cursor-pointer transition-colors <%= idx == 0 ? 'bg-background text-primary shadow-sm ring-1 ring-border/60' : 'bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground' %>">
|
|
93
|
+
<%= ruby_cms_locale_display_name(locale_s) %>
|
|
94
|
+
</button>
|
|
95
|
+
<% end %>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<% @blocks_by_locale.each_with_index do |(locale_s, block), idx| %>
|
|
99
|
+
<div id="locale-panel-<%= locale_s %>"
|
|
100
|
+
role="tabpanel"
|
|
101
|
+
aria-labelledby="locale-tab-<%= locale_s %>"
|
|
102
|
+
class="p-5 <%= idx > 0 ? 'hidden' : '' %>"
|
|
103
|
+
data-locale-panel>
|
|
104
|
+
|
|
105
|
+
<%= hidden_field_tag "content_block[locales][#{locale_s}][locale]", locale_s %>
|
|
106
|
+
|
|
107
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
108
|
+
<div>
|
|
109
|
+
<label class="<%= RubyCms::Admin::AdminResourceCard::LABEL_CLASS %>"><%= t("ruby_cms.admin.content_blocks.title", default: "Title") %></label>
|
|
110
|
+
<%= text_field_tag "content_block[locales][#{locale_s}][title]",
|
|
111
|
+
block.title,
|
|
112
|
+
class: RubyCms::Admin::AdminResourceCard::INPUT_CLASS %>
|
|
113
|
+
</div>
|
|
35
114
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
115
|
+
<div class="sm:col-span-2">
|
|
116
|
+
<label class="<%= RubyCms::Admin::AdminResourceCard::LABEL_CLASS %>"><%= t("ruby_cms.admin.content_blocks.content", default: "Content") %></label>
|
|
117
|
+
<%= text_area_tag "content_block[locales][#{locale_s}][content]",
|
|
118
|
+
block.content,
|
|
119
|
+
rows: 5,
|
|
120
|
+
class: "#{RubyCms::Admin::AdminResourceCard::INPUT_CLASS} resize-y" %>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<% if ::ContentBlock.respond_to?(:action_text_available?) && ::ContentBlock.action_text_available? && block.persisted? && block.respond_to?(:rich_content) %>
|
|
124
|
+
<div class="sm:col-span-2">
|
|
125
|
+
<label class="<%= RubyCms::Admin::AdminResourceCard::LABEL_CLASS %>"><%= t("ruby_cms.admin.content_blocks.rich_content", default: "Rich Content") %></label>
|
|
126
|
+
<%= rich_text_area_tag "content_block[locales][#{locale_s}][rich_content]",
|
|
127
|
+
block.rich_content.present? ? block.rich_content.to_s : "",
|
|
128
|
+
class: "rounded-lg border border-border bg-background text-sm shadow-sm focus:outline-none" %>
|
|
129
|
+
</div>
|
|
130
|
+
<% end %>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<% end %>
|
|
134
|
+
</div>
|
|
43
135
|
<% end %>
|
|
44
136
|
</div>
|
|
45
|
-
</div>
|
|
46
137
|
|
|
47
|
-
|
|
48
|
-
<div class="
|
|
49
|
-
|
|
50
|
-
|
|
138
|
+
<%# ── Details Sidebar (Right 1/3) ── %>
|
|
139
|
+
<div class="<%= RubyCms::Admin::AdminResourceCard::SIDEBAR_CLASS %>">
|
|
140
|
+
<div>
|
|
141
|
+
<h3 class="<%= RubyCms::Admin::AdminResourceCard::SECTION_TITLE_CLASS %>">Details</h3>
|
|
142
|
+
<dl class="space-y-4">
|
|
143
|
+
<div>
|
|
144
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">ID</dt>
|
|
145
|
+
<dd class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_VALUE_CLASS %> font-mono"><%= @content_block.id %></dd>
|
|
146
|
+
</div>
|
|
51
147
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
148
|
+
<div>
|
|
149
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">Key</dt>
|
|
150
|
+
<dd class="mt-1">
|
|
151
|
+
<code class="text-xs bg-muted px-2 py-1 rounded-md font-mono text-foreground"><%= @content_block.key %></code>
|
|
152
|
+
</dd>
|
|
153
|
+
</div>
|
|
56
154
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
</div>
|
|
62
|
-
<% end %>
|
|
63
|
-
</div>
|
|
155
|
+
<div>
|
|
156
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">Locale</dt>
|
|
157
|
+
<dd class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_VALUE_CLASS %>"><%= ruby_cms_locale_display_name(@content_block.locale) %></dd>
|
|
158
|
+
</div>
|
|
64
159
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
<div class="px-3 py-2 border-b border-gray-200 bg-gray-100">
|
|
70
|
-
<span class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium bg-white text-gray-600 ring-1 ring-gray-200">
|
|
71
|
-
<%= ruby_cms_locale_display_name(locale_s) %>
|
|
72
|
-
</span>
|
|
73
|
-
</div>
|
|
160
|
+
<div>
|
|
161
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">Type</dt>
|
|
162
|
+
<dd class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_VALUE_CLASS %>"><%= @content_block.content_type %></dd>
|
|
163
|
+
</div>
|
|
74
164
|
|
|
75
|
-
<div class="p-4 grid grid-cols-2 gap-4">
|
|
76
165
|
<div>
|
|
77
|
-
<
|
|
78
|
-
<
|
|
166
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">Status</dt>
|
|
167
|
+
<dd class="mt-1">
|
|
168
|
+
<% if @content_block.published? %>
|
|
169
|
+
<span class="inline-flex items-center rounded-md bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700 ring-1 ring-inset ring-emerald-200">Published</span>
|
|
170
|
+
<% else %>
|
|
171
|
+
<span class="inline-flex items-center rounded-md bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground ring-1 ring-inset ring-border/60">Draft</span>
|
|
172
|
+
<% end %>
|
|
173
|
+
</dd>
|
|
79
174
|
</div>
|
|
175
|
+
</dl>
|
|
176
|
+
</div>
|
|
80
177
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
178
|
+
<div class="border-t border-border/60 pt-5">
|
|
179
|
+
<h3 class="<%= RubyCms::Admin::AdminResourceCard::SECTION_TITLE_CLASS %> mb-3">Timestamps</h3>
|
|
180
|
+
<dl class="space-y-4">
|
|
181
|
+
<div>
|
|
182
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">Created</dt>
|
|
183
|
+
<dd class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_VALUE_CLASS %>"><%= @content_block.created_at.strftime("%b %d, %Y") %></dd>
|
|
84
184
|
</div>
|
|
85
185
|
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
186
|
+
<div>
|
|
187
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">Last Updated</dt>
|
|
188
|
+
<dd class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_VALUE_CLASS %>"><%= @content_block.updated_at.strftime("%b %d, %Y") %></dd>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<% if @content_block.updated_by.present? %>
|
|
192
|
+
<div>
|
|
193
|
+
<dt class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_LABEL_CLASS %>">Updated By</dt>
|
|
194
|
+
<dd class="<%= RubyCms::Admin::AdminResourceCard::DETAIL_VALUE_CLASS %>"><%= ruby_cms_user_display(@content_block.updated_by) %></dd>
|
|
90
195
|
</div>
|
|
91
196
|
<% end %>
|
|
92
|
-
</
|
|
197
|
+
</dl>
|
|
93
198
|
</div>
|
|
94
|
-
<% end %>
|
|
95
|
-
</div>
|
|
96
|
-
<% else %>
|
|
97
|
-
<div class="grid grid-cols-2 gap-6 p-6">
|
|
98
|
-
<div>
|
|
99
|
-
<div class="text-xs font-medium uppercase tracking-wide text-gray-400 mb-1">Title</div>
|
|
100
|
-
<div class="text-sm text-gray-900"><%= @content_block.title %></div>
|
|
101
|
-
</div>
|
|
102
199
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
200
|
+
<%# ── Preview per locale ── %>
|
|
201
|
+
<% if @blocks_by_locale.present? && @blocks_by_locale.values.any? { |b| b.title.present? || b.content_body.present? } %>
|
|
202
|
+
<div class="border-t border-border/60 pt-5">
|
|
203
|
+
<h3 class="<%= RubyCms::Admin::AdminResourceCard::SECTION_TITLE_CLASS %> mb-3">Content Preview</h3>
|
|
204
|
+
<div class="space-y-3">
|
|
205
|
+
<% @blocks_by_locale.each do |locale_s, block| %>
|
|
206
|
+
<% next if block.title.blank? && block.content_body.blank? %>
|
|
207
|
+
<div class="rounded-lg border border-border/60 bg-muted/30 p-3">
|
|
208
|
+
<span class="inline-flex items-center rounded-md bg-background px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground ring-1 ring-border/60 mb-2"><%= ruby_cms_locale_display_name(locale_s) %></span>
|
|
209
|
+
<% if block.title.present? %>
|
|
210
|
+
<p class="text-xs font-medium text-foreground"><%= block.title %></p>
|
|
211
|
+
<% end %>
|
|
212
|
+
<% if block.content_body.present? %>
|
|
213
|
+
<p class="text-xs text-muted-foreground mt-1 line-clamp-3"><%= block.content_body %></p>
|
|
214
|
+
<% end %>
|
|
215
|
+
</div>
|
|
216
|
+
<% end %>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<% end %>
|
|
106
220
|
</div>
|
|
107
221
|
</div>
|
|
108
|
-
<% end %>
|
|
109
222
|
|
|
110
|
-
|
|
223
|
+
<%# ── Actions Footer ── %>
|
|
224
|
+
<div class="<%= RubyCms::Admin::AdminResourceCard::ACTIONS_CLASS %>">
|
|
225
|
+
<%= link_to t("ruby_cms.admin.common.cancel", default: "Cancel"), ruby_cms_admin_content_blocks_path, class: RubyCms::Admin::AdminResourceCard::CANCEL_CLASS %>
|
|
226
|
+
<%= f.submit t("ruby_cms.admin.common.save", default: "Save Changes"), class: RubyCms::Admin::AdminResourceCard::SUBMIT_CLASS %>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
<% end %>
|