ruby_cms 0.1.1 → 0.1.3
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 +10 -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 +79 -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 +46 -32
- 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/content_blocks_helper.rb +7 -3
- data/app/helpers/ruby_cms/settings_helper.rb +32 -20
- 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/services/ruby_cms/analytics/report.rb +37 -3
- 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/analytics/index.html.erb +103 -108
- data/app/views/ruby_cms/admin/analytics/page_details.html.erb +28 -32
- data/app/views/ruby_cms/admin/analytics/partials/_back_button.html.erb +3 -2
- data/app/views/ruby_cms/admin/analytics/partials/_browser_device.html.erb +34 -20
- data/app/views/ruby_cms/admin/analytics/partials/_daily_activity_chart.html.erb +27 -27
- data/app/views/ruby_cms/admin/analytics/partials/_hourly_activity_chart.html.erb +19 -19
- data/app/views/ruby_cms/admin/analytics/partials/_landing_pages.html.erb +21 -0
- data/app/views/ruby_cms/admin/analytics/partials/_os_stats.html.erb +26 -0
- data/app/views/ruby_cms/admin/analytics/partials/_popular_pages.html.erb +34 -0
- data/app/views/ruby_cms/admin/analytics/partials/_recent_activity.html.erb +16 -14
- data/app/views/ruby_cms/admin/analytics/partials/_security_alert.html.erb +3 -3
- data/app/views/ruby_cms/admin/analytics/partials/_top_referrers.html.erb +14 -14
- data/app/views/ruby_cms/admin/analytics/partials/_top_visitors.html.erb +28 -0
- data/app/views/ruby_cms/admin/analytics/partials/_utm_sources.html.erb +21 -0
- data/app/views/ruby_cms/admin/analytics/visitor_details.html.erb +44 -48
- 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/generators/ruby_cms/install_generator.rb +2 -1
- data/lib/ruby_cms/cli.rb +1 -1
- data/lib/ruby_cms/engine.rb +20 -12
- data/lib/ruby_cms/version.rb +1 -1
- data/lib/ruby_cms.rb +24 -0
- data/lib/tasks/admin.rake +120 -0
- data/log/test.log +7284 -0
- metadata +10 -4
- data/app/views/ruby_cms/admin/content_blocks/edit.html.erb +0 -17
- data/lib/tasks/ruby_cms.rake +0 -27
|
@@ -19,20 +19,22 @@ module RubyCms
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def view_template
|
|
22
|
-
td(class: "w-
|
|
22
|
+
td(class: "w-10 px-4 py-3",
|
|
23
23
|
data: { action: "click->#{@controller_name}#stopPropagation" }) do
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
div(class: "inline-flex items-center justify-center") do
|
|
25
|
+
input(
|
|
26
|
+
type: "checkbox",
|
|
27
|
+
role: "checkbox",
|
|
28
|
+
value: @item_id,
|
|
29
|
+
class: "size-4 rounded border-border/80 text-primary focus:ring-primary/30 focus:ring-offset-0 cursor-pointer transition-colors",
|
|
30
|
+
data: {
|
|
31
|
+
"#{@controller_name}-target": "itemCheckbox",
|
|
32
|
+
item_id: @item_id,
|
|
33
|
+
action: "change->#{@controller_name}#updateSelection"
|
|
34
|
+
},
|
|
35
|
+
aria_label: "Select row"
|
|
36
|
+
)
|
|
37
|
+
end
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
end
|
|
@@ -14,17 +14,19 @@ module RubyCms
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def view_template
|
|
17
|
-
th(class: "w-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
th(class: "w-10 px-4 py-3") do
|
|
18
|
+
div(class: "inline-flex items-center justify-center") do
|
|
19
|
+
input(
|
|
20
|
+
type: "checkbox",
|
|
21
|
+
role: "checkbox",
|
|
22
|
+
class: "size-4 rounded border-border/80 text-primary focus:ring-primary/30 focus:ring-offset-0 cursor-pointer transition-colors",
|
|
23
|
+
data: {
|
|
24
|
+
"#{@controller_name}-target": "selectAllCheckbox",
|
|
25
|
+
action: "change->#{@controller_name}#toggleSelectAll"
|
|
26
|
+
},
|
|
27
|
+
aria_label: "Select all"
|
|
28
|
+
)
|
|
29
|
+
end
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
end
|
|
@@ -63,7 +63,7 @@ module RubyCms
|
|
|
63
63
|
|
|
64
64
|
def dialog_content_attributes
|
|
65
65
|
{
|
|
66
|
-
class: "w-full max-w-md rounded-
|
|
66
|
+
class: "w-full max-w-md rounded-xl border border-border/60 bg-white p-6 shadow-lg ring-1 ring-black/[0.03]",
|
|
67
67
|
data: {
|
|
68
68
|
"#{@controller_name}-target": "dialogContent"
|
|
69
69
|
},
|
|
@@ -74,8 +74,8 @@ module RubyCms
|
|
|
74
74
|
def render_close_button
|
|
75
75
|
button(
|
|
76
76
|
type: "button",
|
|
77
|
-
class: "inline-flex
|
|
78
|
-
"hover:bg-
|
|
77
|
+
class: "inline-flex size-8 items-center justify-center rounded-md text-muted-foreground " \
|
|
78
|
+
"hover:bg-muted hover:text-foreground transition-colors",
|
|
79
79
|
data: {
|
|
80
80
|
action: "click->#{@controller_name}#closeDialog"
|
|
81
81
|
},
|
|
@@ -119,7 +119,7 @@ module RubyCms
|
|
|
119
119
|
def render_header
|
|
120
120
|
h3(
|
|
121
121
|
id: "dialog-title",
|
|
122
|
-
class: "text-base font-semibold text-
|
|
122
|
+
class: "text-base font-semibold text-foreground",
|
|
123
123
|
data: {
|
|
124
124
|
"#{@controller_name}-target": "dialogTitle"
|
|
125
125
|
}
|
|
@@ -128,7 +128,7 @@ module RubyCms
|
|
|
128
128
|
|
|
129
129
|
def render_message
|
|
130
130
|
div(
|
|
131
|
-
class: "mt-3 text-sm text-
|
|
131
|
+
class: "mt-3 text-sm text-muted-foreground space-y-1",
|
|
132
132
|
data: {
|
|
133
133
|
"#{@controller_name}-target": "dialogMessage"
|
|
134
134
|
}
|
|
@@ -149,8 +149,8 @@ module RubyCms
|
|
|
149
149
|
button(
|
|
150
150
|
type: "button",
|
|
151
151
|
class: "inline-flex h-9 items-center justify-center rounded-md border " \
|
|
152
|
-
"border-
|
|
153
|
-
"shadow-sm hover:bg-
|
|
152
|
+
"border-border bg-white px-4 text-sm font-medium text-foreground " \
|
|
153
|
+
"shadow-sm hover:bg-muted transition-colors",
|
|
154
154
|
data: {
|
|
155
155
|
action: "click->#{@controller_name}#closeDialog"
|
|
156
156
|
}
|
|
@@ -160,8 +160,8 @@ module RubyCms
|
|
|
160
160
|
def render_confirm_button
|
|
161
161
|
button(
|
|
162
162
|
type: "button",
|
|
163
|
-
class: "inline-flex h-9 items-center justify-center rounded-md bg-
|
|
164
|
-
"text-sm font-medium text-
|
|
163
|
+
class: "inline-flex h-9 items-center justify-center rounded-md bg-destructive px-4 " \
|
|
164
|
+
"text-sm font-medium text-destructive-foreground shadow-sm hover:bg-destructive/90 transition-colors",
|
|
165
165
|
data: {
|
|
166
166
|
"#{@controller_name}-target": "dialogConfirmButton",
|
|
167
167
|
action: "click->#{@controller_name}#confirmAction"
|
|
@@ -22,7 +22,7 @@ module RubyCms
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def view_template
|
|
25
|
-
thead(class: "bg-
|
|
25
|
+
thead(class: "sticky top-0 z-20 bg-muted/40 [&_tr]:border-b [&_tr]:border-border/60") do
|
|
26
26
|
tr do
|
|
27
27
|
render_bulk_checkbox_header
|
|
28
28
|
render_table_headers
|
|
@@ -51,7 +51,7 @@ module RubyCms
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def th_base_classes
|
|
54
|
-
"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wider text-
|
|
54
|
+
"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground"
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
end
|
|
@@ -31,7 +31,7 @@ module RubyCms
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def view_template
|
|
34
|
-
div(class: "px-
|
|
34
|
+
div(class: "px-5 py-3 border-b border-border/60 bg-white") do
|
|
35
35
|
div(class: "flex flex-wrap items-center justify-between gap-4") do
|
|
36
36
|
render_title_group if @title.present?
|
|
37
37
|
div(class: "flex items-center gap-2 flex-wrap") do
|
|
@@ -47,7 +47,7 @@ module RubyCms
|
|
|
47
47
|
|
|
48
48
|
def render_title_group
|
|
49
49
|
div(class: "min-w-0") do
|
|
50
|
-
h2(class: "text-sm font-semibold text-
|
|
50
|
+
h2(class: "text-sm font-semibold text-foreground") { @title }
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -88,7 +88,7 @@ module RubyCms
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def render_search_icon
|
|
91
|
-
span(class: "absolute left-3 text-
|
|
91
|
+
span(class: "absolute left-3 text-muted-foreground pointer-events-none") do
|
|
92
92
|
svg(class: "h-4 w-4", fill: "none", stroke: "currentColor",
|
|
93
93
|
viewBox: "0 0 24 24") do |s|
|
|
94
94
|
s.path(
|
|
@@ -106,8 +106,8 @@ module RubyCms
|
|
|
106
106
|
type: "search",
|
|
107
107
|
name: @search_param,
|
|
108
108
|
placeholder: "Search",
|
|
109
|
-
class: "h-9 w-full sm:w-72 rounded-md border border-
|
|
110
|
-
"pr-3 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-
|
|
109
|
+
class: "h-9 w-full sm:w-72 rounded-md border border-border bg-white pl-9 " \
|
|
110
|
+
"pr-3 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/20",
|
|
111
111
|
value: search_value,
|
|
112
112
|
data: { action: "input->turbo-frame#submit" }
|
|
113
113
|
)
|
|
@@ -125,15 +125,15 @@ module RubyCms
|
|
|
125
125
|
{
|
|
126
126
|
"blue" => "text-blue-600 hover:bg-blue-50",
|
|
127
127
|
"green" => "text-emerald-600 hover:bg-emerald-50",
|
|
128
|
-
"red" => "text-
|
|
128
|
+
"red" => "text-destructive hover:bg-destructive/10",
|
|
129
129
|
"purple" => "text-violet-600 hover:bg-violet-50",
|
|
130
|
-
"gray" => "text-
|
|
130
|
+
"gray" => "text-muted-foreground hover:bg-muted",
|
|
131
131
|
"teal" => "text-teal-600 hover:bg-teal-50"
|
|
132
132
|
}
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def icon_button_base_classes
|
|
136
|
-
"inline-flex items-center justify-center
|
|
136
|
+
"inline-flex items-center justify-center size-9 rounded-md border border-border " \
|
|
137
137
|
"bg-white shadow-sm transition-colors"
|
|
138
138
|
end
|
|
139
139
|
|
|
@@ -24,7 +24,7 @@ module RubyCms
|
|
|
24
24
|
def view_template
|
|
25
25
|
return unless @pagination[:total_pages] && @pagination[:total_pages] > 1
|
|
26
26
|
|
|
27
|
-
div(class: "px-
|
|
27
|
+
div(class: "px-5 py-3 flex items-center justify-between gap-4 bg-muted/30") do
|
|
28
28
|
render_pagination_info
|
|
29
29
|
render_pagination_controls
|
|
30
30
|
end
|
|
@@ -35,7 +35,7 @@ module RubyCms
|
|
|
35
35
|
def render_pagination_info
|
|
36
36
|
return unless @pagination[:start_item] && @pagination[:end_item] && @pagination[:total_count]
|
|
37
37
|
|
|
38
|
-
div(class: "text-
|
|
38
|
+
div(class: "text-xs text-muted-foreground tabular-nums") { pagination_info_text }
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def pagination_info_text
|
|
@@ -75,14 +75,14 @@ module RubyCms
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def pagination_button_classes
|
|
78
|
-
"inline-flex h-
|
|
79
|
-
"bg-white px-3 text-
|
|
78
|
+
"inline-flex h-8 items-center justify-center rounded-md border border-border " \
|
|
79
|
+
"bg-white px-3 text-xs font-medium text-foreground shadow-sm hover:bg-muted " \
|
|
80
80
|
"transition-colors"
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def pagination_button_disabled_classes
|
|
84
|
-
"inline-flex h-
|
|
85
|
-
"bg-white px-3 text-
|
|
84
|
+
"inline-flex h-8 items-center justify-center rounded-md border border-border " \
|
|
85
|
+
"bg-white px-3 text-xs font-medium text-muted-foreground opacity-50 cursor-not-allowed"
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def render_next_button
|
|
@@ -127,7 +127,7 @@ module RubyCms
|
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def render_ellipsis
|
|
130
|
-
span(class: "px-2 text-
|
|
130
|
+
span(class: "px-2 py-1.5 text-xs font-medium text-muted-foreground select-none") { "…" }
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def render_current_page(page_num)
|
|
@@ -144,8 +144,8 @@ module RubyCms
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def current_page_classes
|
|
147
|
-
"inline-flex h-
|
|
148
|
-
"bg-
|
|
147
|
+
"inline-flex h-8 items-center justify-center rounded-md border border-primary " \
|
|
148
|
+
"bg-primary px-3 text-xs font-medium text-primary-foreground shadow-sm pointer-events-none"
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
def calculate_pages_to_show(current_page, total_pages)
|
|
@@ -73,7 +73,7 @@ module RubyCms
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def build_row_classes
|
|
76
|
-
classes = ["hover:bg-
|
|
76
|
+
classes = ["border-b border-border/40 hover:bg-muted/50 transition-colors"]
|
|
77
77
|
classes << @row_class if @row_class
|
|
78
78
|
classes << "cursor-pointer" if @click_url
|
|
79
79
|
build_classes(classes)
|
|
@@ -84,9 +84,8 @@ module RubyCms
|
|
|
84
84
|
attrs[:item_id] = @data[:item_id] if @data[:item_id]
|
|
85
85
|
|
|
86
86
|
if @click_url
|
|
87
|
-
attrs[:
|
|
88
|
-
attrs[:
|
|
89
|
-
attrs[:action] = "click->clickable-row#navigate"
|
|
87
|
+
attrs[:click_url] = @click_url
|
|
88
|
+
attrs[:action] = "click->#{@controller_name}#rowClick"
|
|
90
89
|
end
|
|
91
90
|
|
|
92
91
|
attrs
|
|
@@ -26,15 +26,12 @@ module RubyCms
|
|
|
26
26
|
|
|
27
27
|
def view_template
|
|
28
28
|
div(
|
|
29
|
-
class: "hidden px-
|
|
29
|
+
class: "hidden bg-primary/5 border-t border-border/60 px-5 py-3 backdrop-blur-sm",
|
|
30
30
|
data: {
|
|
31
31
|
"#{@controller_name}-target": "bulkBar"
|
|
32
32
|
}
|
|
33
33
|
) do
|
|
34
|
-
div(
|
|
35
|
-
class: "flex items-center justify-between gap-3 rounded-lg border " \
|
|
36
|
-
"border-gray-200/80 bg-white px-4 py-3 shadow-sm"
|
|
37
|
-
) do
|
|
34
|
+
div(class: "flex items-center justify-between gap-4 max-w-full") do
|
|
38
35
|
render_selection_info
|
|
39
36
|
render_action_buttons
|
|
40
37
|
end
|
|
@@ -44,35 +41,52 @@ module RubyCms
|
|
|
44
41
|
private
|
|
45
42
|
|
|
46
43
|
def render_selection_info
|
|
47
|
-
div(class: "flex items-center gap-3
|
|
44
|
+
div(class: "flex items-center gap-3") do
|
|
45
|
+
render_selection_left
|
|
46
|
+
div(class: "h-4 w-px bg-border/60")
|
|
47
|
+
render_selection_buttons
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def render_selection_left
|
|
52
|
+
div(class: "flex items-center gap-2") do
|
|
53
|
+
render_selected_count_badge
|
|
54
|
+
span(class: "text-sm font-medium text-foreground") { "selected" }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def render_selected_count_badge
|
|
59
|
+
div(class: "size-6 rounded-full bg-primary/10 flex items-center justify-center") do
|
|
48
60
|
span(
|
|
49
|
-
class: "text-
|
|
61
|
+
class: "text-xs font-bold text-primary tabular-nums",
|
|
50
62
|
data: {
|
|
51
63
|
"#{@controller_name}-target": "selectedCount"
|
|
52
64
|
}
|
|
53
|
-
) { "0
|
|
54
|
-
button(
|
|
55
|
-
type: "button",
|
|
56
|
-
class: "text-sm font-medium text-gray-600 hover:text-gray-900 hover:underline " \
|
|
57
|
-
"transition-colors",
|
|
58
|
-
data: {
|
|
59
|
-
"#{@controller_name}-target": "selectAllButton",
|
|
60
|
-
action: "click->#{@controller_name}#selectAll"
|
|
61
|
-
}
|
|
62
|
-
) { "Select all" }
|
|
63
|
-
button(
|
|
64
|
-
type: "button",
|
|
65
|
-
class: "text-sm font-medium text-gray-600 hover:text-gray-900 hover:underline " \
|
|
66
|
-
"transition-colors",
|
|
67
|
-
data: {
|
|
68
|
-
action: "click->#{@controller_name}#clearSelection"
|
|
69
|
-
}
|
|
70
|
-
) { "Clear selection" }
|
|
65
|
+
) { "0" }
|
|
71
66
|
end
|
|
72
67
|
end
|
|
73
68
|
|
|
69
|
+
def render_selection_buttons
|
|
70
|
+
button(
|
|
71
|
+
type: "button",
|
|
72
|
+
class: "text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
|
|
73
|
+
data: {
|
|
74
|
+
"#{@controller_name}-target": "selectAllButton",
|
|
75
|
+
action: "click->#{@controller_name}#selectAll"
|
|
76
|
+
}
|
|
77
|
+
) { "Select all" }
|
|
78
|
+
|
|
79
|
+
button(
|
|
80
|
+
type: "button",
|
|
81
|
+
class: "text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
|
|
82
|
+
data: {
|
|
83
|
+
action: "click->#{@controller_name}#clearSelection"
|
|
84
|
+
}
|
|
85
|
+
) { "Clear" }
|
|
86
|
+
end
|
|
87
|
+
|
|
74
88
|
def render_action_buttons
|
|
75
|
-
div(class: "flex items-center gap-2
|
|
89
|
+
div(class: "flex items-center gap-2") do
|
|
76
90
|
@bulk_action_buttons.each do |button_config|
|
|
77
91
|
render_custom_action_button(button_config)
|
|
78
92
|
end
|
|
@@ -93,9 +107,9 @@ module RubyCms
|
|
|
93
107
|
end
|
|
94
108
|
|
|
95
109
|
def build_button_class(config)
|
|
96
|
-
base = "inline-flex items-center justify-center rounded-md border border-
|
|
97
|
-
"bg-white px-3 py-
|
|
98
|
-
"hover:bg-
|
|
110
|
+
base = "inline-flex items-center justify-center rounded-md border border-border " \
|
|
111
|
+
"bg-white px-3 py-1.5 text-sm font-medium text-foreground shadow-sm " \
|
|
112
|
+
"hover:bg-muted transition-colors"
|
|
99
113
|
config[:class].present? ? "#{base} #{config[:class]}" : base
|
|
100
114
|
end
|
|
101
115
|
|
|
@@ -118,9 +132,9 @@ module RubyCms
|
|
|
118
132
|
def render_delete_button
|
|
119
133
|
button(
|
|
120
134
|
type: "button",
|
|
121
|
-
class: "inline-flex items-center justify-center rounded-md border border-
|
|
122
|
-
"bg-white px-3 py-
|
|
123
|
-
"hover:bg-
|
|
135
|
+
class: "inline-flex items-center justify-center rounded-md border border-destructive/30 " \
|
|
136
|
+
"bg-white px-3 py-1.5 text-sm font-medium text-destructive shadow-sm " \
|
|
137
|
+
"hover:bg-destructive/10 transition-colors",
|
|
124
138
|
data: {
|
|
125
139
|
action: "click->#{@controller_name}#showActionDialog",
|
|
126
140
|
action_name: "delete",
|
|
@@ -86,10 +86,16 @@ module RubyCms
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def set_cms_locale
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
locale = session[:ruby_cms_locale].presence || session[:admin_locale].presence
|
|
90
|
+
return if locale.blank?
|
|
91
|
+
|
|
92
|
+
locale = locale.to_sym
|
|
93
|
+
return unless I18n.available_locales.include?(locale)
|
|
94
|
+
|
|
95
|
+
# Keep both session keys in sync for host app + engine controllers.
|
|
96
|
+
session[:ruby_cms_locale] = locale
|
|
97
|
+
session[:admin_locale] = locale
|
|
98
|
+
I18n.locale = locale
|
|
93
99
|
end
|
|
94
100
|
|
|
95
101
|
# Resolve parameter key for model params
|
|
@@ -23,7 +23,7 @@ module RubyCms
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def show
|
|
26
|
-
@blocks_by_locale =
|
|
26
|
+
@blocks_by_locale = load_blocks_by_locale_for_edit
|
|
27
27
|
respond_with_block(@content_block)
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -37,6 +37,7 @@ module RubyCms
|
|
|
37
37
|
|
|
38
38
|
def edit
|
|
39
39
|
@blocks_by_locale = load_blocks_by_locale_for_edit
|
|
40
|
+
render :show
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
def create
|
|
@@ -135,12 +136,12 @@ module RubyCms
|
|
|
135
136
|
def handle_locale_update_errors(errors)
|
|
136
137
|
@content_block.errors.add(:base, errors.join("; "))
|
|
137
138
|
@blocks_by_locale = load_blocks_by_locale_for_edit
|
|
138
|
-
render :
|
|
139
|
+
render :show, status: :unprocessable_content
|
|
139
140
|
end
|
|
140
141
|
|
|
141
142
|
def update_single_block
|
|
142
143
|
@content_block.record_update_by(current_user_cms)
|
|
143
|
-
save_and_respond(@content_block, :
|
|
144
|
+
save_and_respond(@content_block, :show)
|
|
144
145
|
end
|
|
145
146
|
|
|
146
147
|
def save_and_respond(block, failure_view)
|
|
@@ -7,9 +7,10 @@ module RubyCms
|
|
|
7
7
|
before_action :ensure_authenticated, only: [:update]
|
|
8
8
|
|
|
9
9
|
def update
|
|
10
|
-
locale = params[:locale].to_sym
|
|
10
|
+
locale = params[:locale].to_s.presence&.to_sym
|
|
11
11
|
if I18n.available_locales.include?(locale)
|
|
12
12
|
session[:ruby_cms_locale] = locale
|
|
13
|
+
session[:admin_locale] = locale
|
|
13
14
|
I18n.locale = locale
|
|
14
15
|
end
|
|
15
16
|
|
|
@@ -17,14 +17,11 @@ module RubyCms
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def create
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
if params[:template].present?
|
|
21
|
+
apply_template
|
|
22
|
+
else
|
|
23
|
+
grant_individual_permission
|
|
24
24
|
end
|
|
25
|
-
rescue ActiveRecord::RecordInvalid
|
|
26
|
-
redirect_to ruby_cms_admin_user_permissions_path(@user),
|
|
27
|
-
alert: t("ruby_cms.admin.user_permissions.could_not_grant")
|
|
28
25
|
end
|
|
29
26
|
|
|
30
27
|
def destroy
|
|
@@ -54,6 +51,27 @@ module RubyCms
|
|
|
54
51
|
def user_class
|
|
55
52
|
Object.const_get(Rails.application.config.ruby_cms.user_class_name.presence || "User")
|
|
56
53
|
end
|
|
54
|
+
|
|
55
|
+
def apply_template
|
|
56
|
+
template_key = params[:template].to_sym
|
|
57
|
+
RubyCms::UserPermission.where(user: @user).destroy_all
|
|
58
|
+
RubyCms::Permission.apply_template!(@user, template_key)
|
|
59
|
+
@user.make_admin! if @user.respond_to?(:make_admin!) && !@user.admin?
|
|
60
|
+
redirect_to ruby_cms_admin_user_permissions_path(@user),
|
|
61
|
+
notice: "Applied #{RubyCms::Permission.templates.dig(template_key, :label)} template."
|
|
62
|
+
rescue ArgumentError => e
|
|
63
|
+
redirect_to ruby_cms_admin_user_permissions_path(@user), alert: e.message
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def grant_individual_permission
|
|
67
|
+
permission = RubyCms::Permission.find(params[:permission_id])
|
|
68
|
+
RubyCms::UserPermission.find_or_create_by!(user: @user, permission: permission)
|
|
69
|
+
redirect_to ruby_cms_admin_user_permissions_path(@user),
|
|
70
|
+
notice: t("ruby_cms.admin.user_permissions.granted")
|
|
71
|
+
rescue ActiveRecord::RecordInvalid
|
|
72
|
+
redirect_to ruby_cms_admin_user_permissions_path(@user),
|
|
73
|
+
alert: t("ruby_cms.admin.user_permissions.could_not_grant")
|
|
74
|
+
end
|
|
57
75
|
end
|
|
58
76
|
end
|
|
59
77
|
end
|
|
@@ -114,14 +114,14 @@ module RubyCms
|
|
|
114
114
|
def build_css_class(options)
|
|
115
115
|
user_class = options.delete(:class)
|
|
116
116
|
return user_class.to_s if user_class.present? && !visual_editor_preview_edit_mode?
|
|
117
|
-
return user_class.to_s
|
|
117
|
+
return user_class.to_s unless visual_editor_preview_edit_mode?
|
|
118
118
|
|
|
119
119
|
["ruby_cms-content-block", "content-block", user_class].compact.join(" ")
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def build_data_attributes(key, options)
|
|
123
123
|
user_data = options.delete(:data).to_h
|
|
124
|
-
return user_data
|
|
124
|
+
return user_data unless visual_editor_preview_edit_mode?
|
|
125
125
|
|
|
126
126
|
{ content_key: key, block_id: key.to_s }.merge(user_data)
|
|
127
127
|
end
|
|
@@ -133,7 +133,11 @@ module RubyCms
|
|
|
133
133
|
return false unless controller.respond_to?(:action_name) && controller.action_name.to_s == "page_preview"
|
|
134
134
|
|
|
135
135
|
begin
|
|
136
|
-
|
|
136
|
+
if controller.instance_variable_defined?(:@edit_mode)
|
|
137
|
+
controller.instance_variable_get(:@edit_mode) ? true : false
|
|
138
|
+
else
|
|
139
|
+
true
|
|
140
|
+
end
|
|
137
141
|
rescue StandardError
|
|
138
142
|
true
|
|
139
143
|
end
|
|
@@ -34,6 +34,16 @@ module RubyCms
|
|
|
34
34
|
key.tr("_", " ").humanize
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# SVG path fragment for a nav item (matches sidebar), or nil.
|
|
38
|
+
def settings_nav_visibility_icon(entry)
|
|
39
|
+
key_str = entry.key.to_s
|
|
40
|
+
return nil unless key_str.start_with?("nav_show_")
|
|
41
|
+
|
|
42
|
+
nav_key = key_str.delete_prefix("nav_show_")
|
|
43
|
+
item = RubyCms.nav_registry.find {|e| e[:key].to_s == nav_key }
|
|
44
|
+
item&.dig(:icon)
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
def render_setting_field(entry:, value:, tab:)
|
|
38
48
|
case entry.type.to_sym
|
|
39
49
|
when :integer
|
|
@@ -50,13 +60,13 @@ module RubyCms
|
|
|
50
60
|
private
|
|
51
61
|
|
|
52
62
|
def input_base_classes
|
|
53
|
-
"w-full h-9 rounded-
|
|
54
|
-
"shadow-sm focus:outline-none focus:ring-2 focus:ring-
|
|
63
|
+
"w-full h-9 rounded-lg border border-border bg-background px-3 text-sm text-foreground " \
|
|
64
|
+
"shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/20"
|
|
55
65
|
end
|
|
56
66
|
|
|
57
67
|
def textarea_base_classes
|
|
58
|
-
"w-full rounded-
|
|
59
|
-
"shadow-sm focus:outline-none focus:ring-2 focus:ring-
|
|
68
|
+
"w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground " \
|
|
69
|
+
"shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/20"
|
|
60
70
|
end
|
|
61
71
|
|
|
62
72
|
def render_integer_setting_field(entry:, value:, tab:)
|
|
@@ -75,31 +85,33 @@ module RubyCms
|
|
|
75
85
|
|
|
76
86
|
def render_boolean_setting_field(entry:, value:, tab:)
|
|
77
87
|
checked = ActiveModel::Type::Boolean.new.cast(value)
|
|
88
|
+
input_id = setting_input_id(entry)
|
|
89
|
+
|
|
78
90
|
hidden = hidden_field_tag("preferences[#{entry.key}]", "false")
|
|
79
91
|
checkbox = check_box_tag(
|
|
80
92
|
"preferences[#{entry.key}]",
|
|
81
93
|
"true",
|
|
82
94
|
checked,
|
|
83
|
-
id:
|
|
95
|
+
id: input_id,
|
|
84
96
|
class: "peer sr-only",
|
|
85
97
|
data: autosave_data(entry.key, tab)
|
|
86
98
|
)
|
|
87
|
-
label = content_tag(
|
|
88
|
-
:label,
|
|
89
|
-
"",
|
|
90
|
-
for: setting_input_id(entry),
|
|
91
|
-
class: "relative inline-flex h-6 w-11 cursor-pointer items-center rounded-full " \
|
|
92
|
-
"bg-gray-200 transition-colors peer-checked:bg-teal-600"
|
|
93
|
-
) do
|
|
94
|
-
content_tag(
|
|
95
|
-
:span,
|
|
96
|
-
"",
|
|
97
|
-
class: "inline-block h-5 w-5 transform rounded-full bg-white shadow-sm transition " \
|
|
98
|
-
"translate-x-0.5 peer-checked:translate-x-5"
|
|
99
|
-
)
|
|
100
|
-
end
|
|
101
99
|
|
|
102
|
-
content_tag(:
|
|
100
|
+
track = content_tag(:label, "", for: input_id,
|
|
101
|
+
class: "relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full " \
|
|
102
|
+
"border-2 border-transparent bg-input transition-colors " \
|
|
103
|
+
"peer-checked:bg-primary " \
|
|
104
|
+
"peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring " \
|
|
105
|
+
"peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-background")
|
|
106
|
+
|
|
107
|
+
thumb = content_tag(:span, "",
|
|
108
|
+
class: "pointer-events-none absolute left-[3px] top-1/2 -translate-y-1/2 h-5 w-5 rounded-full " \
|
|
109
|
+
"bg-background shadow-lg ring-0 transition-transform " \
|
|
110
|
+
"translate-x-0 peer-checked:translate-x-5")
|
|
111
|
+
|
|
112
|
+
content_tag(:div, class: "relative inline-flex items-center shrink-0") do
|
|
113
|
+
safe_join([hidden, checkbox, track, thumb])
|
|
114
|
+
end
|
|
103
115
|
end
|
|
104
116
|
|
|
105
117
|
def render_json_setting_field(entry:, value:, tab:)
|