easy-admin-rails 0.2.6 → 0.2.7
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/app/assets/builds/easy_admin.base.js +7 -0
- data/app/assets/builds/easy_admin.base.js.map +2 -2
- data/app/assets/builds/easy_admin.css +207 -35
- data/app/components/easy_admin/fields/form/belongs_to_component.rb +0 -1
- data/app/components/easy_admin/form_layout_component.rb +553 -0
- data/app/components/easy_admin/permissions/user_role_permissions_component.rb +1 -3
- data/app/components/easy_admin/show_layout_component.rb +694 -24
- data/app/controllers/easy_admin/application_controller.rb +0 -5
- data/app/controllers/easy_admin/batch_actions_controller.rb +0 -1
- data/app/controllers/easy_admin/concerns/inline_field_editing.rb +4 -11
- data/app/controllers/easy_admin/concerns/resource_loading.rb +10 -9
- data/app/controllers/easy_admin/concerns/resource_pagination.rb +3 -0
- data/app/controllers/easy_admin/dashboards_controller.rb +0 -1
- data/app/controllers/easy_admin/resources_controller.rb +1 -5
- data/app/controllers/easy_admin/row_actions_controller.rb +1 -4
- data/app/helpers/easy_admin/fields_helper.rb +8 -22
- data/app/javascript/easy_admin/controllers/infinite_scroll_controller.js +12 -0
- data/app/views/easy_admin/resources/edit.html.erb +2 -2
- data/app/views/easy_admin/resources/new.html.erb +2 -2
- data/app/views/easy_admin/resources/show.html.erb +3 -1
- data/lib/easy_admin/field.rb +3 -2
- data/lib/easy_admin/layouts/builders/base_layout_builder.rb +245 -0
- data/lib/easy_admin/layouts/builders/form_layout_builder.rb +208 -0
- data/lib/easy_admin/layouts/builders/index_layout_builder.rb +22 -0
- data/lib/easy_admin/layouts/builders/show_layout_builder.rb +199 -0
- data/lib/easy_admin/layouts/dsl.rb +200 -0
- data/lib/easy_admin/layouts/layout_context.rb +189 -0
- data/lib/easy_admin/layouts/nodes/base_node.rb +88 -0
- data/lib/easy_admin/layouts/nodes/divider.rb +27 -0
- data/lib/easy_admin/layouts/nodes/field_node.rb +57 -0
- data/lib/easy_admin/layouts/nodes/grid.rb +60 -0
- data/lib/easy_admin/layouts/nodes/render_node.rb +41 -0
- data/lib/easy_admin/layouts/nodes/root.rb +25 -0
- data/lib/easy_admin/layouts/nodes/section.rb +46 -0
- data/lib/easy_admin/layouts/nodes/spacer.rb +17 -0
- data/lib/easy_admin/layouts/nodes/stubs.rb +109 -0
- data/lib/easy_admin/layouts/nodes/tab.rb +40 -0
- data/lib/easy_admin/layouts/nodes/tabs.rb +40 -0
- data/lib/easy_admin/layouts.rb +28 -0
- data/lib/easy_admin/permissions/resource_permissions.rb +1 -5
- data/lib/easy_admin/resource/base.rb +2 -2
- data/lib/easy_admin/resource/dsl.rb +2 -11
- data/lib/easy_admin/resource/field_registry.rb +58 -2
- data/lib/easy_admin/resource.rb +0 -9
- data/lib/easy_admin/resource_modules.rb +21 -4
- data/lib/easy_admin/version.rb +1 -1
- data/lib/generators/easy_admin/permissions/install_generator.rb +0 -10
- data/lib/generators/easy_admin/permissions/templates/migrations/create_permission_tables.rb +33 -3
- metadata +21 -9
- data/lib/easy_admin/resource/form_builder.rb +0 -123
- data/lib/easy_admin/resource/layout_builder.rb +0 -249
- data/lib/easy_admin/resource/show_builder.rb +0 -359
- data/lib/generators/easy_admin/permissions/templates/migrations/update_users_for_permissions.rb +0 -6
- data/lib/generators/easy_admin/rbac/rbac_generator.rb +0 -244
- data/lib/generators/easy_admin/rbac/templates/add_rbac_to_admin_users.rb +0 -23
- data/lib/generators/easy_admin/rbac/templates/super_admin.rb +0 -34
@@ -0,0 +1,553 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
class FormLayoutComponent < BaseComponent
|
3
|
+
include EasyAdmin::FieldsHelper
|
4
|
+
|
5
|
+
def initialize(resource_class:, form:, record: nil)
|
6
|
+
@resource_class = resource_class
|
7
|
+
@form = form
|
8
|
+
@record = record
|
9
|
+
end
|
10
|
+
|
11
|
+
def view_template
|
12
|
+
has_custom = @resource_class.has_custom_form_layout?
|
13
|
+
|
14
|
+
if has_custom
|
15
|
+
render_custom_layout
|
16
|
+
else
|
17
|
+
render_default_layout
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def render_custom_layout
|
24
|
+
layout_type, layout_content = @resource_class.form_layout_definition
|
25
|
+
|
26
|
+
case layout_type
|
27
|
+
when :ast
|
28
|
+
render_ast_layout(layout_content)
|
29
|
+
when :component
|
30
|
+
render layout_content
|
31
|
+
else
|
32
|
+
render_default_layout
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_ast_layout(root_node)
|
37
|
+
if root_node
|
38
|
+
render_children(root_node)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_children(node)
|
43
|
+
return unless node&.children
|
44
|
+
|
45
|
+
node.children.each do |child|
|
46
|
+
render_node(child)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def render_node(node)
|
51
|
+
case node
|
52
|
+
when EasyAdmin::Layouts::Nodes::Row
|
53
|
+
render_row_node(node)
|
54
|
+
when EasyAdmin::Layouts::Nodes::Column
|
55
|
+
render_column_node(node)
|
56
|
+
when EasyAdmin::Layouts::Nodes::Card
|
57
|
+
render_card_node(node)
|
58
|
+
when EasyAdmin::Layouts::Nodes::Section
|
59
|
+
render_section_node(node)
|
60
|
+
when EasyAdmin::Layouts::Nodes::Grid
|
61
|
+
render_grid_node(node)
|
62
|
+
when EasyAdmin::Layouts::Nodes::FieldNode
|
63
|
+
render_field_node(node)
|
64
|
+
when EasyAdmin::Layouts::Nodes::Heading
|
65
|
+
render_heading_node(node)
|
66
|
+
when EasyAdmin::Layouts::Nodes::Divider
|
67
|
+
render_divider_node(node)
|
68
|
+
when EasyAdmin::Layouts::Nodes::Tabs
|
69
|
+
render_tabs_node(node)
|
70
|
+
when EasyAdmin::Layouts::Nodes::Tab
|
71
|
+
nil
|
72
|
+
when EasyAdmin::Layouts::Nodes::Content
|
73
|
+
render_content_node(node)
|
74
|
+
when EasyAdmin::Layouts::Nodes::Spacer
|
75
|
+
render_spacer_node(node)
|
76
|
+
when EasyAdmin::Layouts::Nodes::RenderNode
|
77
|
+
render_render_node(node)
|
78
|
+
else
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def render_row_node(node)
|
84
|
+
classes = ["grid", "items-stretch"]
|
85
|
+
|
86
|
+
# Check if any child column has explicit size - if so, use 12-column grid
|
87
|
+
has_sized_columns = node.children.any? { |child|
|
88
|
+
child.is_a?(EasyAdmin::Layouts::Nodes::Column) && child.attributes[:size]
|
89
|
+
}
|
90
|
+
|
91
|
+
if has_sized_columns
|
92
|
+
# Use 12-column grid for Bootstrap-style column sizing
|
93
|
+
classes << "grid-cols-1 md:grid-cols-12"
|
94
|
+
elsif node.attributes[:columns]
|
95
|
+
# Use simple equal-width columns
|
96
|
+
columns = node.attributes[:columns]
|
97
|
+
case columns
|
98
|
+
when 1
|
99
|
+
classes << "grid-cols-1"
|
100
|
+
when 2
|
101
|
+
classes << "grid-cols-1 md:grid-cols-2"
|
102
|
+
when 3
|
103
|
+
classes << "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
104
|
+
when 4
|
105
|
+
classes << "grid-cols-1 md:grid-cols-2 lg:grid-cols-4"
|
106
|
+
else
|
107
|
+
classes << "grid-cols-1 md:grid-cols-#{[columns, 12].min}"
|
108
|
+
end
|
109
|
+
else
|
110
|
+
classes << "grid-cols-1"
|
111
|
+
end
|
112
|
+
|
113
|
+
# Add spacing with Tailwind gap classes
|
114
|
+
spacing = node.attributes[:spacing] || "medium"
|
115
|
+
case spacing
|
116
|
+
when "small", "sm"
|
117
|
+
classes << "gap-2"
|
118
|
+
when "medium", "md"
|
119
|
+
classes << "gap-4"
|
120
|
+
when "large", "lg"
|
121
|
+
classes << "gap-6"
|
122
|
+
else
|
123
|
+
classes << "gap-4"
|
124
|
+
end
|
125
|
+
|
126
|
+
div(class: classes.join(" ")) do
|
127
|
+
render_children(node)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def render_column_node(node)
|
132
|
+
classes = ["flex", "flex-col", "h-full"]
|
133
|
+
|
134
|
+
# Handle column spanning in Tailwind CSS Grid
|
135
|
+
if node.attributes[:size]
|
136
|
+
size = node.attributes[:size]
|
137
|
+
# On mobile, all columns take full width, on desktop use specified size
|
138
|
+
case size
|
139
|
+
when 1
|
140
|
+
classes << "col-span-1 md:col-span-1"
|
141
|
+
when 2
|
142
|
+
classes << "col-span-1 md:col-span-2"
|
143
|
+
when 3
|
144
|
+
classes << "col-span-1 md:col-span-3"
|
145
|
+
when 4
|
146
|
+
classes << "col-span-1 md:col-span-4"
|
147
|
+
when 5
|
148
|
+
classes << "col-span-1 md:col-span-5"
|
149
|
+
when 6
|
150
|
+
classes << "col-span-1 md:col-span-6"
|
151
|
+
when 7
|
152
|
+
classes << "col-span-1 md:col-span-7"
|
153
|
+
when 8
|
154
|
+
classes << "col-span-1 md:col-span-8"
|
155
|
+
when 9
|
156
|
+
classes << "col-span-1 md:col-span-9"
|
157
|
+
when 10
|
158
|
+
classes << "col-span-1 md:col-span-10"
|
159
|
+
when 11
|
160
|
+
classes << "col-span-1 md:col-span-11"
|
161
|
+
when 12
|
162
|
+
classes << "col-span-1 md:col-span-12"
|
163
|
+
else
|
164
|
+
classes << "col-span-full"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
div(class: classes.join(" ")) do
|
169
|
+
render_children(node)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def render_card_node(node)
|
174
|
+
# Build card classes with Tailwind
|
175
|
+
card_classes = ["bg-white", "rounded-lg", "shadow-sm", "border", "mb-4", "flex-1", "flex", "flex-col", "h-full"]
|
176
|
+
|
177
|
+
# Add color styling with Tailwind
|
178
|
+
if node.attributes[:color]
|
179
|
+
case node.attributes[:color]
|
180
|
+
when "primary"
|
181
|
+
card_classes << "border-blue-200"
|
182
|
+
when "secondary"
|
183
|
+
card_classes << "border-gray-200"
|
184
|
+
when "success"
|
185
|
+
card_classes << "border-green-200"
|
186
|
+
when "info"
|
187
|
+
card_classes << "border-cyan-200"
|
188
|
+
when "warning"
|
189
|
+
card_classes << "border-yellow-200"
|
190
|
+
when "danger"
|
191
|
+
card_classes << "border-red-200"
|
192
|
+
when "light"
|
193
|
+
card_classes << "border-gray-100"
|
194
|
+
when "dark"
|
195
|
+
card_classes << "border-gray-800"
|
196
|
+
else
|
197
|
+
card_classes << "border-gray-200"
|
198
|
+
end
|
199
|
+
else
|
200
|
+
card_classes << "border-gray-200"
|
201
|
+
end
|
202
|
+
|
203
|
+
div(class: card_classes.join(" ")) do
|
204
|
+
# Card header
|
205
|
+
if node.attributes[:title]
|
206
|
+
header_classes = ["px-6", "py-4", "border-b", "border-gray-200"]
|
207
|
+
|
208
|
+
# Add header color styling with Tailwind
|
209
|
+
if node.attributes[:color]
|
210
|
+
case node.attributes[:color]
|
211
|
+
when "primary"
|
212
|
+
header_classes << "bg-blue-50"
|
213
|
+
when "secondary"
|
214
|
+
header_classes << "bg-gray-50"
|
215
|
+
when "success"
|
216
|
+
header_classes << "bg-green-50"
|
217
|
+
when "info"
|
218
|
+
header_classes << "bg-cyan-50"
|
219
|
+
when "warning"
|
220
|
+
header_classes << "bg-yellow-50"
|
221
|
+
when "danger"
|
222
|
+
header_classes << "bg-red-50"
|
223
|
+
when "light"
|
224
|
+
header_classes << "bg-gray-25"
|
225
|
+
when "dark"
|
226
|
+
header_classes << "bg-gray-800 text-white"
|
227
|
+
else
|
228
|
+
header_classes << "bg-gray-50"
|
229
|
+
end
|
230
|
+
else
|
231
|
+
header_classes << "bg-gray-50"
|
232
|
+
end
|
233
|
+
|
234
|
+
div(class: header_classes.join(" ")) do
|
235
|
+
h3(class: "text-lg font-semibold text-gray-900") { node.attributes[:title] }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Card body
|
240
|
+
div(class: "px-6 py-4 flex-1") do
|
241
|
+
render_children(node)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def render_section_node(node)
|
247
|
+
div(class: "mb-6") do
|
248
|
+
if node.attributes[:title]
|
249
|
+
h3(class: "text-lg font-medium leading-6 text-gray-900 mb-4") do
|
250
|
+
node.attributes[:title]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
render_children(node)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def render_grid_node(node)
|
258
|
+
columns = node.attributes[:columns] || 2
|
259
|
+
classes = ["grid"]
|
260
|
+
|
261
|
+
# Add responsive column classes with Tailwind CSS Grid
|
262
|
+
case columns
|
263
|
+
when 1
|
264
|
+
classes << "grid-cols-1"
|
265
|
+
when 2
|
266
|
+
classes << "grid-cols-1 md:grid-cols-2"
|
267
|
+
when 3
|
268
|
+
classes << "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
269
|
+
when 4
|
270
|
+
classes << "grid-cols-1 md:grid-cols-2 lg:grid-cols-4"
|
271
|
+
else
|
272
|
+
classes << "grid-cols-1 md:grid-cols-#{[columns, 6].min}"
|
273
|
+
end
|
274
|
+
|
275
|
+
# Add spacing
|
276
|
+
spacing = node.attributes[:spacing] || "medium"
|
277
|
+
classes << spacing_class(spacing)
|
278
|
+
|
279
|
+
div(class: classes.join(" ")) do
|
280
|
+
render_children(node)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def render_field_node(node)
|
285
|
+
base_field_config = @resource_class.find_field_config(node.field_name) || {}
|
286
|
+
field_config = base_field_config.merge(node.attributes).merge({
|
287
|
+
name: node.field_name,
|
288
|
+
label: node.attributes[:label] || base_field_config[:label] || node.field_name.to_s.humanize,
|
289
|
+
type: node.attributes[:type] || base_field_config[:type]
|
290
|
+
})
|
291
|
+
|
292
|
+
div(class: "mb-6") do
|
293
|
+
render_form_field(field_config)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def render_form_field(field_config)
|
298
|
+
if field_config[:type] == :has_many
|
299
|
+
render_has_many_form_field(field_config)
|
300
|
+
else
|
301
|
+
field_definition = {
|
302
|
+
name: field_config[:name],
|
303
|
+
type: field_config[:type] || :text,
|
304
|
+
field_type: field_config[:type] || :text,
|
305
|
+
label: field_config[:label],
|
306
|
+
required: field_config[:required],
|
307
|
+
help_text: field_config[:help_text],
|
308
|
+
options: field_config[:options],
|
309
|
+
multiple: field_config[:multiple]
|
310
|
+
}.merge(field_config)
|
311
|
+
|
312
|
+
result = render_field(field_definition, action: :form, form: @form, record: @record)
|
313
|
+
|
314
|
+
unsafe_raw result
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def render_has_many_form_field(field_config)
|
319
|
+
div(class: "mb-6") do
|
320
|
+
div(class: "block text-sm font-medium text-gray-700 mb-2") do
|
321
|
+
field_config[:label]
|
322
|
+
end
|
323
|
+
|
324
|
+
if @record
|
325
|
+
association = @record.public_send(field_config[:name])
|
326
|
+
count = association.respond_to?(:count) ? association.count : 0
|
327
|
+
|
328
|
+
div(class: "px-4 py-3 bg-gray-50 border border-gray-200 rounded-md") do
|
329
|
+
p(class: "text-sm text-gray-600") do
|
330
|
+
"#{count} #{field_config[:name].to_s.humanize.downcase} associated with this #{@resource_class.resource_name.humanize.downcase}"
|
331
|
+
end
|
332
|
+
|
333
|
+
if field_config[:help_text]
|
334
|
+
p(class: "mt-2 text-xs text-gray-500") { field_config[:help_text] }
|
335
|
+
end
|
336
|
+
|
337
|
+
p(class: "mt-2 text-xs text-gray-400") do
|
338
|
+
"Manage #{field_config[:name].to_s.humanize.downcase} through the #{field_config[:name].to_s.humanize} section"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
else
|
342
|
+
div(class: "px-4 py-3 bg-gray-50 border border-gray-200 rounded-md") do
|
343
|
+
p(class: "text-sm text-gray-600") do
|
344
|
+
"#{field_config[:name].to_s.humanize} will be available after saving this #{@resource_class.resource_name.humanize.downcase}"
|
345
|
+
end
|
346
|
+
|
347
|
+
if field_config[:help_text]
|
348
|
+
p(class: "mt-2 text-xs text-gray-500") { field_config[:help_text] }
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
def render_heading_node(node)
|
357
|
+
level = node.attributes[:level] || 2
|
358
|
+
tag_name = "h#{level}"
|
359
|
+
classes = ["mb-3", "font-semibold"]
|
360
|
+
|
361
|
+
# Add appropriate text sizing based on heading level
|
362
|
+
case level
|
363
|
+
when 1
|
364
|
+
classes << "text-xl text-gray-900"
|
365
|
+
when 2
|
366
|
+
classes << "text-lg text-gray-900"
|
367
|
+
when 3
|
368
|
+
classes << "text-base text-gray-800"
|
369
|
+
when 4
|
370
|
+
classes << "text-sm text-gray-800"
|
371
|
+
else
|
372
|
+
classes << "text-sm text-gray-700"
|
373
|
+
end
|
374
|
+
|
375
|
+
send(tag_name, class: classes.join(" ")) { node.text }
|
376
|
+
end
|
377
|
+
|
378
|
+
def render_divider_node(node)
|
379
|
+
margin = node.attributes[:margin] || 4
|
380
|
+
hr(class: "my-#{margin} border-gray-200")
|
381
|
+
end
|
382
|
+
|
383
|
+
def render_tabs_node(node)
|
384
|
+
tabs_id = node.attributes[:id] || "form-tabs-#{SecureRandom.hex(4)}"
|
385
|
+
|
386
|
+
div(class: "form-tabs",
|
387
|
+
data: {
|
388
|
+
controller: "form-tabs",
|
389
|
+
action: "tab:selected@window->form-tabs#handleTabSelection"
|
390
|
+
}) do
|
391
|
+
|
392
|
+
render_tab_navigation(node, tabs_id)
|
393
|
+
|
394
|
+
render_tab_content(node, tabs_id)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def render_tab_navigation(tabs_node, tabs_id)
|
399
|
+
nav(class: "border-b border-gray-200 mb-4 sm:mb-6") do
|
400
|
+
# Desktop tabs
|
401
|
+
ul(class: "flex flex-wrap -mb-px text-sm font-medium text-center", role: "tablist") do
|
402
|
+
tabs_node.children.each_with_index do |tab_node, index|
|
403
|
+
next unless tab_node.is_a?(EasyAdmin::Layouts::Nodes::Tab)
|
404
|
+
|
405
|
+
li(class: "mr-2", role: "presentation") do
|
406
|
+
button(
|
407
|
+
class: tab_button_classes(index == 0),
|
408
|
+
id: "#{tab_node.attributes[:name]}-tab",
|
409
|
+
data: {
|
410
|
+
form_tabs_target: "tabButton",
|
411
|
+
tab_id: tab_node.attributes[:name],
|
412
|
+
action: "click->form-tabs#switchTab"
|
413
|
+
},
|
414
|
+
type: "button",
|
415
|
+
role: "tab"
|
416
|
+
) do
|
417
|
+
if tab_node.attributes[:icon]
|
418
|
+
span(class: "inline-flex items-center") do
|
419
|
+
unsafe_raw(tab_icon(tab_node.attributes[:icon]))
|
420
|
+
span(class: "ml-2") { tab_node.label }
|
421
|
+
end
|
422
|
+
else
|
423
|
+
plain tab_node.label
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def render_tab_content(tabs_node, tabs_id)
|
433
|
+
Rails.logger.info "🔍 [FormLayout] render_tab_content started"
|
434
|
+
div(class: "tab-content") do
|
435
|
+
tabs_node.children.each_with_index do |tab_node, index|
|
436
|
+
next unless tab_node.is_a?(EasyAdmin::Layouts::Nodes::Tab)
|
437
|
+
|
438
|
+
div(
|
439
|
+
class: tab_panel_classes(index == 0),
|
440
|
+
id: "#{tab_node.attributes[:name]}-panel",
|
441
|
+
data: { form_tabs_target: "tabPanel" },
|
442
|
+
role: "tabpanel"
|
443
|
+
) do
|
444
|
+
div(class: "bg-white shadow-sm rounded-lg border border-gray-200") do
|
445
|
+
div(class: "px-4 py-5 sm:p-6") do
|
446
|
+
if tab_node.label && !tab_node.label.empty?
|
447
|
+
h3(class: "text-lg font-medium leading-6 text-gray-900 mb-6") do
|
448
|
+
tab_node.label
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
div(class: "grid grid-cols-1 gap-6") do
|
453
|
+
render_children(tab_node)
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
def render_content_node(node)
|
463
|
+
if node.block
|
464
|
+
context = EasyAdmin::Layouts::LayoutContext.new(
|
465
|
+
record: @record,
|
466
|
+
resource_class: @resource_class,
|
467
|
+
form_builder: @form
|
468
|
+
)
|
469
|
+
result = context.instance_exec(&node.block)
|
470
|
+
if result.respond_to?(:call)
|
471
|
+
render result
|
472
|
+
else
|
473
|
+
unsafe_raw result.to_s
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def render_default_layout
|
479
|
+
div(class: "bg-white shadow-sm rounded-lg border border-gray-200") do
|
480
|
+
div(class: "px-4 py-5 sm:p-6") do
|
481
|
+
div(class: "grid grid-cols-1 gap-6") do
|
482
|
+
@resource_class.fields_config.each do |field_config|
|
483
|
+
next if field_config[:readonly] || field_config[:form] == false
|
484
|
+
render_form_field(field_config)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
def tab_button_classes(active = false)
|
492
|
+
base_classes = "inline-flex items-center p-4 border-b-2 rounded-t-lg transition-colors duration-200"
|
493
|
+
|
494
|
+
if active
|
495
|
+
active_classes = "text-blue-600 border-blue-600"
|
496
|
+
else
|
497
|
+
inactive_classes = "text-gray-500 border-transparent hover:text-gray-600 hover:border-gray-300"
|
498
|
+
end
|
499
|
+
|
500
|
+
"#{base_classes} #{active ? active_classes : inactive_classes}"
|
501
|
+
end
|
502
|
+
|
503
|
+
def tab_panel_classes(active = false)
|
504
|
+
base_classes = "tab-panel"
|
505
|
+
visibility_classes = active ? "block" : "hidden"
|
506
|
+
|
507
|
+
"#{base_classes} #{visibility_classes}"
|
508
|
+
end
|
509
|
+
|
510
|
+
def tab_icon(icon_name)
|
511
|
+
case icon_name.to_sym
|
512
|
+
when :user
|
513
|
+
'<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>'
|
514
|
+
when :security
|
515
|
+
'<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>'
|
516
|
+
when :settings, :info
|
517
|
+
'<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>'
|
518
|
+
else
|
519
|
+
'<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>'
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
def render_spacer_node(node)
|
524
|
+
div(class: "my-#{node.attributes[:size] || 4}")
|
525
|
+
end
|
526
|
+
|
527
|
+
def render_render_node(node)
|
528
|
+
if node.component_class
|
529
|
+
begin
|
530
|
+
component = node.component_class.constantize.new(**node.attributes)
|
531
|
+
render component
|
532
|
+
rescue => e
|
533
|
+
div(class: "bg-yellow-50 border border-yellow-200 text-yellow-800 px-4 py-3 rounded-md") do
|
534
|
+
"Component class not found: #{node.component_class}"
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
def spacing_class(spacing)
|
541
|
+
case spacing
|
542
|
+
when "small", "sm"
|
543
|
+
"gap-2"
|
544
|
+
when "medium", "md"
|
545
|
+
"gap-4"
|
546
|
+
when "large", "lg"
|
547
|
+
"gap-6"
|
548
|
+
else
|
549
|
+
"gap-4"
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
@@ -14,8 +14,6 @@ module EasyAdmin
|
|
14
14
|
# Get actual permissions from permissions_cache
|
15
15
|
@user_permissions = get_user_permissions_from_cache(user)
|
16
16
|
@available_resources = EasyAdmin::Permissions.available_resources
|
17
|
-
|
18
|
-
Rails.logger.debug "UserRolePermissionsComponent: user=#{@user&.id}, role=#{@current_role&.name}, cached_permissions=#{@user_permissions.size}"
|
19
17
|
end
|
20
18
|
|
21
19
|
def view_template
|
@@ -192,4 +190,4 @@ module EasyAdmin
|
|
192
190
|
end
|
193
191
|
end
|
194
192
|
end
|
195
|
-
end
|
193
|
+
end
|