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
|
@@ -1,220 +1,259 @@
|
|
|
1
1
|
<%
|
|
2
2
|
nav_main_section = "main"
|
|
3
|
-
nav_bottom_section = "Settings"
|
|
4
3
|
nav_entries = ruby_cms_nav_entries
|
|
5
4
|
nav_main_entries = nav_entries.select { |e| (e[:section].to_s.presence || nav_main_section) == nav_main_section }
|
|
6
5
|
nav_bottom_entries = nav_entries.select { |e| (e[:section].to_s.presence || nav_main_section) != nav_main_section }
|
|
7
|
-
nav_item_entries = @active_tab == "navigation" ? @entries_for_tab.reject { |e| e.key == "nav_order" } : []
|
|
6
|
+
nav_item_entries = @active_tab == "navigation" ? @entries_for_tab.reject { |e| e.key.to_s == "nav_order" } : []
|
|
8
7
|
nav_sub = params[:nav_sub].to_s.presence || "order"
|
|
9
8
|
entries_for_grid = @active_tab == "navigation" ? [] : @entries_for_tab
|
|
10
9
|
is_navigation = @active_tab == "navigation"
|
|
11
10
|
settings_url = ruby_cms_admin_settings_path
|
|
12
11
|
nav_order_url = ruby_cms_admin_settings_nav_order_path
|
|
13
12
|
%>
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
<%= render RubyCms::Admin::AdminPageHeader.new(
|
|
15
15
|
title: t("ruby_cms.admin.settings.title", default: "Settings"),
|
|
16
|
-
subtitle: t("ruby_cms.admin.settings.subtitle", default: "
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
subtitle: t("ruby_cms.admin.settings.subtitle", default: "Preferences for your workspace."),
|
|
17
|
+
breadcrumbs: [
|
|
18
|
+
{ label: t("ruby_cms.admin.breadcrumb.admin", default: "Admin"), url: ruby_cms_admin_root_path },
|
|
19
|
+
{ label: t("ruby_cms.admin.settings.title", default: "Settings") }
|
|
20
|
+
]
|
|
21
|
+
) %>
|
|
22
|
+
|
|
23
|
+
<%# Single card: category rail | separator | content (Todoist-like) %>
|
|
24
|
+
<div class="rounded-2xl border border-border bg-card shadow-sm ring-1 ring-black/[0.03] overflow-hidden">
|
|
25
|
+
<div class="flex flex-col lg:flex-row lg:min-h-[min(100vh-12rem,52rem)]">
|
|
26
|
+
<%# Left: sections (icons from TAB_CONFIG) %>
|
|
27
|
+
<aside class="shrink-0 border-b border-border lg:w-56 lg:border-b-0 lg:border-r bg-muted/25">
|
|
28
|
+
<div class="p-3 sm:p-4">
|
|
29
|
+
<p class="mb-3 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">
|
|
30
|
+
<%= t("ruby_cms.admin.settings.categories_title", default: "Sections") %>
|
|
25
31
|
</p>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<span class="h-2 w-2 rounded-full bg-gray-900" aria-hidden="true"></span>
|
|
32
|
+
<nav class="space-y-0.5" aria-label="Settings categories">
|
|
33
|
+
<% @categories.each do |category| %>
|
|
34
|
+
<% cfg = settings_tab_config(category) %>
|
|
35
|
+
<% category_s = category.to_s %>
|
|
36
|
+
<% active = (@active_tab.to_s == category_s) %>
|
|
37
|
+
<%= link_to ruby_cms_admin_settings_path(tab: category_s),
|
|
38
|
+
class: [
|
|
39
|
+
"group flex items-center gap-3 rounded-xl px-2.5 py-2 text-[15px] leading-snug transition-colors no-underline",
|
|
40
|
+
active ? "bg-background text-foreground font-medium shadow-sm ring-1 ring-border" : "text-muted-foreground hover:bg-background/80 hover:text-foreground"
|
|
41
|
+
].join(" ") do %>
|
|
42
|
+
<span class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-lg leading-none #{active ? 'bg-muted' : 'bg-muted/60 group-hover:bg-muted'}">
|
|
43
|
+
<%= cfg[:icon] %>
|
|
44
|
+
</span>
|
|
45
|
+
<span class="min-w-0 flex-1"><%= settings_tab_label(category_s) %></span>
|
|
41
46
|
<% end %>
|
|
42
47
|
<% end %>
|
|
43
|
-
|
|
44
|
-
</
|
|
48
|
+
</nav>
|
|
49
|
+
</div>
|
|
45
50
|
</aside>
|
|
46
51
|
|
|
47
|
-
<%# Right
|
|
48
|
-
<div class="
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
<%# Right: active section %>
|
|
53
|
+
<div class="flex min-w-0 flex-1 flex-col bg-background">
|
|
54
|
+
<% tab_description = settings_tab_description(@active_tab) %>
|
|
55
|
+
|
|
56
|
+
<div class="border-b border-border/80 px-5 py-4 sm:px-6 sm:py-5">
|
|
51
57
|
<% if tab_description.present? %>
|
|
52
|
-
<p class="text-sm text-
|
|
58
|
+
<p class="text-sm leading-relaxed text-muted-foreground"><%= tab_description %></p>
|
|
53
59
|
<% end %>
|
|
54
60
|
|
|
55
61
|
<% if is_navigation %>
|
|
56
|
-
<div class="
|
|
57
|
-
<div
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</div>
|
|
62
|
+
<div class="mt-4 flex flex-col gap-3 sm:mt-3 sm:flex-row sm:items-center sm:justify-between">
|
|
63
|
+
<div>
|
|
64
|
+
<h2 class="text-base font-semibold tracking-tight text-foreground">
|
|
65
|
+
<%= t("ruby_cms.admin.settings.navigation_title", default: "Navigation") %>
|
|
66
|
+
</h2>
|
|
67
|
+
<p class="mt-0.5 text-sm text-muted-foreground">
|
|
68
|
+
<%= t("ruby_cms.admin.settings.navigation_hint", default: "Reorder items or choose what appears in the sidebar.") %>
|
|
69
|
+
</p>
|
|
70
|
+
</div>
|
|
71
|
+
<%# Full page links — avoids broken turbo-frame partial loads %>
|
|
72
|
+
<div class="inline-flex shrink-0 rounded-xl bg-muted/60 p-1 ring-1 ring-border/50">
|
|
73
|
+
<%= link_to ruby_cms_admin_settings_path(tab: "navigation", nav_sub: "order"),
|
|
74
|
+
class: "rounded-lg px-4 py-2 text-sm font-medium no-underline transition-all #{nav_sub == 'order' ? 'bg-background text-foreground shadow-sm ring-1 ring-black/[0.06]' : 'text-muted-foreground hover:text-foreground'}" do %>
|
|
75
|
+
<%= t("ruby_cms.admin.settings.nav_order_tab", default: "Order") %>
|
|
76
|
+
<% end %>
|
|
77
|
+
<%= link_to ruby_cms_admin_settings_path(tab: "navigation", nav_sub: "items"),
|
|
78
|
+
class: "rounded-lg px-4 py-2 text-sm font-medium no-underline transition-all #{nav_sub == 'items' ? 'bg-background text-foreground shadow-sm ring-1 ring-black/[0.06]' : 'text-muted-foreground hover:text-foreground'}" do %>
|
|
79
|
+
<%= t("ruby_cms.admin.settings.nav_items_tab", default: "Visibility") %>
|
|
80
|
+
<% end %>
|
|
76
81
|
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<% elsif entries_for_grid.any? %>
|
|
84
|
+
<h2 class="text-base font-semibold tracking-tight text-foreground"><%= settings_tab_label(@active_tab) %></h2>
|
|
85
|
+
<p class="mt-1 text-sm text-muted-foreground">
|
|
86
|
+
<%= t("ruby_cms.admin.settings.form_hint", default: "Toggles save immediately; other fields use Save below.") %>
|
|
87
|
+
</p>
|
|
88
|
+
<% end %>
|
|
89
|
+
</div>
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
<div class="flex flex-1 flex-col px-5 py-5 sm:px-6 sm:py-6">
|
|
92
|
+
<% if is_navigation %>
|
|
93
|
+
<% if nav_sub == "order" %>
|
|
94
|
+
<%= form_with url: settings_url, method: :patch, local: true, html: { id: "nav_order_form", class: "flex flex-1 flex-col space-y-6", data: { turbo: false } } do %>
|
|
95
|
+
<%= hidden_field_tag :tab, "navigation" %>
|
|
96
|
+
<div class="grid flex-1 gap-6 lg:grid-cols-2">
|
|
97
|
+
<div>
|
|
98
|
+
<div class="mb-2 flex items-baseline justify-between gap-2">
|
|
99
|
+
<h3 class="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">
|
|
100
|
+
<%= t("ruby_cms.nav.main", default: "Main") %>
|
|
101
|
+
</h3>
|
|
102
|
+
<span class="text-xs text-muted-foreground"><%= t("ruby_cms.admin.settings.drag_hint", default: "Drag to reorder") %></span>
|
|
103
|
+
</div>
|
|
104
|
+
<ul class="divide-y divide-border rounded-xl border border-border bg-muted/10"
|
|
105
|
+
data-list-type="main"
|
|
106
|
+
data-controller="ruby-cms--nav-order-sortable"
|
|
107
|
+
data-ruby-cms--nav-order-sortable-settings-url-value="<%= nav_order_url %>">
|
|
108
|
+
<% nav_main_entries.each do |entry| %>
|
|
109
|
+
<li class="flex items-center gap-3 px-3 py-3 transition-colors hover:bg-muted/30" data-key="<%= entry[:key] %>" draggable="true">
|
|
110
|
+
<span class="cursor-grab select-none text-muted-foreground/80" aria-hidden="true">⋮⋮</span>
|
|
111
|
+
<% if entry[:icon].present? %>
|
|
112
|
+
<span class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-border/80 bg-background text-muted-foreground">
|
|
113
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
114
|
+
<%= ruby_cms_safe_svg_fragment(entry[:icon]) %>
|
|
115
|
+
</svg>
|
|
116
|
+
</span>
|
|
99
117
|
<% end %>
|
|
100
|
-
|
|
101
|
-
|
|
118
|
+
<span class="min-w-0 flex-1 text-sm font-medium text-foreground"><%= entry[:label] %></span>
|
|
119
|
+
<%= hidden_field_tag "nav_order_main[]", entry[:key], id: nil %>
|
|
120
|
+
</li>
|
|
121
|
+
<% end %>
|
|
122
|
+
</ul>
|
|
123
|
+
</div>
|
|
102
124
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
data-list-type="bottom"
|
|
110
|
-
data-controller="ruby-cms--nav-order-sortable"
|
|
111
|
-
data-ruby-cms--nav-order-sortable-settings-url-value="<%= nav_order_url %>">
|
|
112
|
-
<% nav_bottom_entries.each do |entry| %>
|
|
113
|
-
<li class="flex items-center gap-3 px-3 py-2.5 hover:bg-gray-50 transition-colors" data-key="<%= entry[:key] %>" draggable="true">
|
|
114
|
-
<span class="cursor-grab text-gray-400 select-none text-sm" aria-hidden="true">⋮⋮</span>
|
|
115
|
-
<span class="flex-1 text-sm font-medium text-gray-900"><%= entry[:label] %></span>
|
|
116
|
-
<%= hidden_field_tag "nav_order_bottom[]", entry[:key], id: nil %>
|
|
117
|
-
</li>
|
|
118
|
-
<% end %>
|
|
119
|
-
</ul>
|
|
120
|
-
</div>
|
|
125
|
+
<div>
|
|
126
|
+
<div class="mb-2 flex items-baseline justify-between gap-2">
|
|
127
|
+
<h3 class="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">
|
|
128
|
+
<%= t("ruby_cms.nav.settings", default: "Settings") %>
|
|
129
|
+
</h3>
|
|
130
|
+
<span class="text-xs text-muted-foreground"><%= t("ruby_cms.admin.settings.drag_hint", default: "Drag to reorder") %></span>
|
|
121
131
|
</div>
|
|
132
|
+
<ul class="divide-y divide-border rounded-xl border border-border bg-muted/10"
|
|
133
|
+
data-list-type="bottom"
|
|
134
|
+
data-controller="ruby-cms--nav-order-sortable"
|
|
135
|
+
data-ruby-cms--nav-order-sortable-settings-url-value="<%= nav_order_url %>">
|
|
136
|
+
<% nav_bottom_entries.each do |entry| %>
|
|
137
|
+
<li class="flex items-center gap-3 px-3 py-3 transition-colors hover:bg-muted/30" data-key="<%= entry[:key] %>" draggable="true">
|
|
138
|
+
<span class="cursor-grab select-none text-muted-foreground/80" aria-hidden="true">⋮⋮</span>
|
|
139
|
+
<% if entry[:icon].present? %>
|
|
140
|
+
<span class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-border/80 bg-background text-muted-foreground">
|
|
141
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
142
|
+
<%= ruby_cms_safe_svg_fragment(entry[:icon]) %>
|
|
143
|
+
</svg>
|
|
144
|
+
</span>
|
|
145
|
+
<% end %>
|
|
146
|
+
<span class="min-w-0 flex-1 text-sm font-medium text-foreground"><%= entry[:label] %></span>
|
|
147
|
+
<%= hidden_field_tag "nav_order_bottom[]", entry[:key], id: nil %>
|
|
148
|
+
</li>
|
|
149
|
+
<% end %>
|
|
150
|
+
</ul>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
122
153
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
154
|
+
<p class="rounded-lg border border-dashed border-border bg-muted/20 px-3 py-2.5 text-sm text-muted-foreground">
|
|
155
|
+
<%= t("ruby_cms.admin.settings.nav_order_hint", default: "Changes save automatically when you move an item.") %>
|
|
156
|
+
</p>
|
|
157
|
+
<% end %>
|
|
158
|
+
<% else %>
|
|
159
|
+
<%# Visibility: same icons as main sidebar %>
|
|
160
|
+
<div class="space-y-4">
|
|
161
|
+
<div>
|
|
162
|
+
<h3 class="text-sm font-semibold text-foreground"><%= t("ruby_cms.admin.settings.show_in_sidebar", default: "Show in sidebar") %></h3>
|
|
163
|
+
<p class="mt-1 text-sm text-muted-foreground">
|
|
164
|
+
<%= t("ruby_cms.admin.settings.show_in_sidebar_hint", default: "Turn sections on or off. Changes save when you toggle.") %>
|
|
165
|
+
</p>
|
|
166
|
+
</div>
|
|
134
167
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
</li>
|
|
168
|
+
<% if nav_item_entries.any? %>
|
|
169
|
+
<ul class="divide-y divide-border overflow-hidden rounded-xl border border-border">
|
|
170
|
+
<% nav_item_entries.each do |entry| %>
|
|
171
|
+
<% icon_frag = settings_nav_visibility_icon(entry) %>
|
|
172
|
+
<li class="flex items-center gap-3 px-3 py-3.5 transition-colors hover:bg-muted/25 sm:gap-4 sm:px-4">
|
|
173
|
+
<% if icon_frag.present? %>
|
|
174
|
+
<span class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border border-border bg-muted/30 text-muted-foreground">
|
|
175
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
176
|
+
<%= ruby_cms_safe_svg_fragment(icon_frag) %>
|
|
177
|
+
</svg>
|
|
178
|
+
</span>
|
|
179
|
+
<% else %>
|
|
180
|
+
<span class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border border-dashed border-border bg-muted/20 text-xs font-semibold uppercase text-muted-foreground">
|
|
181
|
+
<%= setting_label(entry).to_s.first(2) %>
|
|
182
|
+
</span>
|
|
151
183
|
<% end %>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
184
|
+
<div class="min-w-0 flex-1">
|
|
185
|
+
<span class="block text-sm font-medium text-foreground"><%= setting_label(entry) %></span>
|
|
186
|
+
<% if entry.description.present? %>
|
|
187
|
+
<span class="mt-0.5 block text-xs leading-relaxed text-muted-foreground"><%= entry.description %></span>
|
|
188
|
+
<% end %>
|
|
189
|
+
</div>
|
|
190
|
+
<div class="shrink-0">
|
|
191
|
+
<%= render_setting_field(entry: entry, value: @values[entry.key], tab: @active_tab) %>
|
|
192
|
+
</div>
|
|
193
|
+
</li>
|
|
155
194
|
<% end %>
|
|
156
|
-
</
|
|
195
|
+
</ul>
|
|
196
|
+
<% else %>
|
|
197
|
+
<p class="rounded-xl border border-dashed border-border bg-muted/15 py-10 text-center text-sm text-muted-foreground">
|
|
198
|
+
<%= t("ruby_cms.admin.settings.no_nav_items", default: "No items to configure here.") %>
|
|
199
|
+
</p>
|
|
157
200
|
<% end %>
|
|
158
201
|
</div>
|
|
159
|
-
|
|
160
|
-
<% else %>
|
|
161
|
-
<% if entries_for_grid.any? %>
|
|
162
|
-
<div class="rounded-lg border border-gray-200/80 bg-white shadow-sm overflow-hidden">
|
|
163
|
-
<div class="px-6 py-4 border-b border-gray-100">
|
|
164
|
-
<h2 class="text-base font-semibold text-gray-900"><%= settings_tab_label(@active_tab) %></h2>
|
|
165
|
-
<p class="mt-1 text-sm text-gray-500"><%= t("ruby_cms.admin.settings.form_hint", default: "Changes are saved when you click Save Changes, or automatically when toggles change.") %></p>
|
|
166
|
-
</div>
|
|
202
|
+
<% end %>
|
|
167
203
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
<%= render_setting_field(entry: entry, value: @values[entry.key], tab: @active_tab) %>
|
|
178
|
-
</div>
|
|
204
|
+
<% elsif entries_for_grid.any? %>
|
|
205
|
+
<%= form_with url: settings_url, method: :patch, local: true, data: { turbo: false }, class: "flex flex-1 flex-col" do %>
|
|
206
|
+
<%= hidden_field_tag :tab, @active_tab %>
|
|
207
|
+
<div class="grid flex-1 gap-x-6 gap-y-6 sm:grid-cols-2 xl:grid-cols-3">
|
|
208
|
+
<% entries_for_grid.each do |entry| %>
|
|
209
|
+
<div class="min-w-0 space-y-2">
|
|
210
|
+
<%= label_tag "pref_#{entry.key}", setting_label(entry), class: "block text-sm font-medium text-foreground" %>
|
|
211
|
+
<% if entry.description.present? %>
|
|
212
|
+
<p class="text-xs leading-relaxed text-muted-foreground"><%= entry.description %></p>
|
|
179
213
|
<% end %>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
<div class="px-6 py-4 border-t border-gray-100 bg-gray-50 flex flex-wrap items-center justify-between gap-3">
|
|
183
|
-
<p class="text-xs text-gray-500">
|
|
184
|
-
<%= t("ruby_cms.admin.settings.save_hint", default: "Tip: toggles autosave, other fields save on submit.") %>
|
|
185
|
-
</p>
|
|
186
|
-
<%= submit_tag t("ruby_cms.admin.settings.save_changes", default: "Save Changes"),
|
|
187
|
-
class: "inline-flex h-9 items-center justify-center rounded-md bg-gray-900 px-4 text-sm font-medium text-white shadow-sm hover:bg-gray-800 transition-colors" %>
|
|
214
|
+
<%= render_setting_field(entry: entry, value: @values[entry.key], tab: @active_tab) %>
|
|
188
215
|
</div>
|
|
189
216
|
<% end %>
|
|
190
217
|
</div>
|
|
191
|
-
|
|
192
|
-
<div class="
|
|
193
|
-
<p class="text-
|
|
218
|
+
|
|
219
|
+
<div class="mt-8 flex flex-col-reverse gap-3 border-t border-border pt-5 sm:flex-row sm:items-center sm:justify-between">
|
|
220
|
+
<p class="text-xs text-muted-foreground">
|
|
221
|
+
<%= t("ruby_cms.admin.settings.save_hint", default: "Toggles autosave; text fields need Save.") %>
|
|
222
|
+
</p>
|
|
223
|
+
<%= submit_tag t("ruby_cms.admin.settings.save_changes", default: "Save changes"),
|
|
224
|
+
class: "inline-flex h-10 cursor-pointer items-center justify-center rounded-xl bg-primary px-5 text-sm font-semibold text-primary-foreground shadow-sm transition-colors hover:bg-primary/90" %>
|
|
194
225
|
</div>
|
|
195
226
|
<% end %>
|
|
227
|
+
|
|
228
|
+
<% else %>
|
|
229
|
+
<p class="flex-1 rounded-xl border border-dashed border-border bg-muted/15 py-12 text-center text-sm text-muted-foreground">
|
|
230
|
+
<%= t("ruby_cms.admin.settings.no_settings", default: "Nothing to configure in this section yet.") %>
|
|
231
|
+
</p>
|
|
196
232
|
<% end %>
|
|
233
|
+
</div>
|
|
197
234
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
235
|
+
<%# Subtle reset row (same card, not a separate “danger zone”) %>
|
|
236
|
+
<%
|
|
237
|
+
reset_path_opts = { tab: @active_tab }
|
|
238
|
+
reset_path_opts[:nav_sub] = params[:nav_sub].presence if @active_tab.to_s == "navigation"
|
|
239
|
+
%>
|
|
240
|
+
<div class="mt-auto border-t border-border bg-muted/10 px-5 py-3 sm:px-6">
|
|
241
|
+
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
|
242
|
+
<p class="text-xs text-muted-foreground">
|
|
243
|
+
<%= t("ruby_cms.admin.settings.reset_inline_hint", default: "Restore factory defaults (all sections).") %>
|
|
244
|
+
</p>
|
|
245
|
+
<%= form_with url: ruby_cms_admin_settings_reset_defaults_path(reset_path_opts),
|
|
246
|
+
method: :post,
|
|
247
|
+
local: true,
|
|
248
|
+
data: { turbo: false, turbo_confirm: t("ruby_cms.admin.settings.reset_confirm", default: "Reset all settings to default values?") },
|
|
249
|
+
class: "shrink-0" do %>
|
|
250
|
+
<%= button_tag type: :submit,
|
|
251
|
+
class: "w-full cursor-pointer rounded-lg border border-border bg-background px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground sm:w-auto" do %>
|
|
252
|
+
<%= t("ruby_cms.admin.settings.reset_defaults_short", default: "Reset defaults") %>
|
|
214
253
|
<% end %>
|
|
215
|
-
|
|
254
|
+
<% end %>
|
|
216
255
|
</div>
|
|
217
|
-
|
|
256
|
+
</div>
|
|
218
257
|
</div>
|
|
219
258
|
</div>
|
|
220
|
-
|
|
259
|
+
</div>
|
|
@@ -8,8 +8,38 @@
|
|
|
8
8
|
<%= link_to "Back to users", ruby_cms_admin_users_path, class: "text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors" %>
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
|
+
<% if RubyCms::Permission.templates.any? %>
|
|
12
|
+
<div class="rounded-lg border border-gray-200/80 bg-white p-6 shadow-sm">
|
|
13
|
+
<p class="text-sm font-semibold text-gray-900 mb-1">Permission templates</p>
|
|
14
|
+
<p class="text-xs text-gray-500 mb-4">Apply a predefined set of permissions. This replaces all current permissions.</p>
|
|
15
|
+
<div class="flex flex-wrap gap-2">
|
|
16
|
+
<% active_templates = RubyCms::Permission.matching_templates(@user) %>
|
|
17
|
+
<% RubyCms::Permission.templates.each do |key, tmpl| %>
|
|
18
|
+
<% is_active = active_templates.include?(key) %>
|
|
19
|
+
<%= form_with url: ruby_cms_admin_user_permissions_path(@user),
|
|
20
|
+
method: :post,
|
|
21
|
+
class: "inline",
|
|
22
|
+
data: { turbo_confirm: "Apply the #{tmpl[:label]} template? This replaces all current permissions." } do |f| %>
|
|
23
|
+
<%= f.hidden_field :template, value: key %>
|
|
24
|
+
<%= f.submit tmpl[:label],
|
|
25
|
+
class: "inline-flex items-center gap-1.5 rounded-lg px-3.5 py-2 text-sm font-medium transition-colors cursor-pointer border #{
|
|
26
|
+
is_active ?
|
|
27
|
+
'bg-gray-900 text-white border-gray-900' :
|
|
28
|
+
'bg-white text-gray-700 border-gray-200 hover:bg-gray-50'
|
|
29
|
+
}" %>
|
|
30
|
+
<% end %>
|
|
31
|
+
<% end %>
|
|
32
|
+
</div>
|
|
33
|
+
<% if active_templates.any? %>
|
|
34
|
+
<p class="mt-3 text-xs text-gray-500">
|
|
35
|
+
Active: <span class="font-medium text-gray-900"><%= active_templates.map {|k| RubyCms::Permission.templates[k][:label] }.join(", ") %></span>
|
|
36
|
+
</p>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
<% end %>
|
|
40
|
+
|
|
11
41
|
<div class="rounded-lg border border-gray-200/80 bg-white p-6 shadow-sm">
|
|
12
|
-
<p class="text-sm font-semibold text-gray-900">Grant permission</p>
|
|
42
|
+
<p class="text-sm font-semibold text-gray-900">Grant individual permission</p>
|
|
13
43
|
<%= form_with url: ruby_cms_admin_user_permissions_path(@user), method: :post, class: "mt-4 flex items-end gap-2" do |f| %>
|
|
14
44
|
<div class="flex-1">
|
|
15
45
|
<%= f.select :permission_id,
|
|
@@ -17,7 +47,7 @@
|
|
|
17
47
|
{ include_blank: "Select…" },
|
|
18
48
|
class: "w-full h-9 rounded-md border border-gray-200 bg-white px-3 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-teal-200" %>
|
|
19
49
|
</div>
|
|
20
|
-
<%= f.submit "Grant", class: "h-9 inline-flex items-center justify-center rounded-md bg-gray-900 px-4 text-sm font-medium text-white shadow-sm hover:bg-gray-800 transition-colors" %>
|
|
50
|
+
<%= f.submit "Grant", class: "h-9 inline-flex items-center justify-center rounded-md bg-gray-900 px-4 text-sm font-medium text-white shadow-sm hover:bg-gray-800 transition-colors cursor-pointer" %>
|
|
21
51
|
<% end %>
|
|
22
52
|
</div>
|
|
23
53
|
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
<%= tag.td (user.try(:email_address) || user.try(:email) || user.to_s), class: "px-6 py-3 text-gray-700" %>
|
|
5
5
|
<%= tag.td class: "px-6 py-3 text-right" do %>
|
|
6
6
|
<div class="flex items-center justify-end gap-2">
|
|
7
|
-
<%= link_to "Permissions",
|
|
7
|
+
<%= link_to "Permissions",
|
|
8
|
+
ruby_cms_admin_user_permissions_path(user),
|
|
9
|
+
class: "text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors",
|
|
10
|
+
data: { turbo_frame: "_top" } %>
|
|
8
11
|
<%= render RubyCms::Admin::BulkActionTable::BulkActionTableActions.new(
|
|
9
12
|
delete_path: ruby_cms_admin_user_path(user),
|
|
10
13
|
item_id: user.id,
|
data/config/locales/en.yml
CHANGED
|
@@ -16,6 +16,8 @@ en:
|
|
|
16
16
|
nav:
|
|
17
17
|
settings: "Settings"
|
|
18
18
|
admin:
|
|
19
|
+
breadcrumb:
|
|
20
|
+
admin: "Admin"
|
|
19
21
|
base:
|
|
20
22
|
authentication_required: "Authentication required."
|
|
21
23
|
not_authorized: "Not authorized."
|
|
@@ -38,6 +40,8 @@ en:
|
|
|
38
40
|
updated: "Setting '%{key}' updated successfully."
|
|
39
41
|
defaults_reset: "All settings have been reset to default values."
|
|
40
42
|
reset_defaults: "Reset to Defaults"
|
|
43
|
+
reset_defaults_short: "Reset defaults"
|
|
44
|
+
reset_inline_hint: "Restore factory defaults for the entire admin (all sections)."
|
|
41
45
|
reset_confirm: "Reset all settings to default values?"
|
|
42
46
|
no_settings: "No settings in this category"
|
|
43
47
|
analytics:
|