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,208 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
module Layouts
|
3
|
+
module Builders
|
4
|
+
# Builder specifically for form layouts
|
5
|
+
class FormLayoutBuilder < BaseLayoutBuilder
|
6
|
+
def initialize(resource_class: nil)
|
7
|
+
super(:form, resource_class: resource_class)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Form-specific DSL methods
|
11
|
+
|
12
|
+
# Add fieldset for grouping form fields
|
13
|
+
def fieldset(legend = nil, **attributes, &block)
|
14
|
+
fieldset_node = Nodes::Fieldset.new(legend, attributes)
|
15
|
+
|
16
|
+
if block_given?
|
17
|
+
with_container(fieldset_node, &block)
|
18
|
+
else
|
19
|
+
add_node(fieldset_node)
|
20
|
+
end
|
21
|
+
|
22
|
+
fieldset_node
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add form actions (submit, cancel, etc.)
|
26
|
+
def form_actions(**attributes, &block)
|
27
|
+
actions_node = Nodes::FormActions.new(attributes)
|
28
|
+
add_node(actions_node)
|
29
|
+
|
30
|
+
if block_given?
|
31
|
+
@current_container_stack.push(actions_node)
|
32
|
+
form_actions_builder = FormActionsBuilder.new(self, actions_node)
|
33
|
+
form_actions_builder.instance_exec(&block)
|
34
|
+
@current_container_stack.pop
|
35
|
+
end
|
36
|
+
|
37
|
+
actions_node
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add inline fields (multiple fields in one row)
|
41
|
+
def inline_fields(**attributes, &block)
|
42
|
+
inline_node = Nodes::InlineFields.new(attributes)
|
43
|
+
|
44
|
+
if block_given?
|
45
|
+
with_container(inline_node, &block)
|
46
|
+
else
|
47
|
+
add_node(inline_node)
|
48
|
+
end
|
49
|
+
|
50
|
+
inline_node
|
51
|
+
end
|
52
|
+
|
53
|
+
# Add conditional fields
|
54
|
+
def conditional_fields(condition:, **attributes, &block)
|
55
|
+
conditional_node = Nodes::ConditionalFields.new(
|
56
|
+
condition: condition,
|
57
|
+
**attributes
|
58
|
+
)
|
59
|
+
|
60
|
+
if block_given?
|
61
|
+
with_container(conditional_node, &block)
|
62
|
+
else
|
63
|
+
add_node(conditional_node)
|
64
|
+
end
|
65
|
+
|
66
|
+
conditional_node
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add nested fields for associations
|
70
|
+
def nested_fields(association, **attributes, &block)
|
71
|
+
nested_node = Nodes::NestedFields.new(
|
72
|
+
association,
|
73
|
+
attributes
|
74
|
+
)
|
75
|
+
|
76
|
+
if block_given?
|
77
|
+
with_container(nested_node, &block)
|
78
|
+
else
|
79
|
+
add_node(nested_node)
|
80
|
+
end
|
81
|
+
|
82
|
+
nested_node
|
83
|
+
end
|
84
|
+
|
85
|
+
# Add help text
|
86
|
+
def help_text(text, **attributes)
|
87
|
+
help_node = Nodes::HelpText.new(text, attributes)
|
88
|
+
add_node(help_node)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Add form errors summary
|
92
|
+
def errors_summary(**attributes)
|
93
|
+
errors_node = Nodes::ErrorsSummary.new(attributes)
|
94
|
+
add_node(errors_node)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Override field method to include form-specific options
|
98
|
+
def field(name, **options)
|
99
|
+
# Set default form field options
|
100
|
+
options[:required] = true if options[:required].nil? && required_field?(name)
|
101
|
+
options[:readonly] = true if readonly_field?(name)
|
102
|
+
|
103
|
+
super(name, **options)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Convenience methods for specific field types
|
107
|
+
def text_field(name, **options)
|
108
|
+
field(name, field_type: :text, **options)
|
109
|
+
end
|
110
|
+
|
111
|
+
def email_field(name, **options)
|
112
|
+
field(name, field_type: :email, **options)
|
113
|
+
end
|
114
|
+
|
115
|
+
def textarea_field(name, **options)
|
116
|
+
field(name, field_type: :textarea, **options)
|
117
|
+
end
|
118
|
+
|
119
|
+
def boolean_field(name, **options)
|
120
|
+
field(name, field_type: :boolean, **options)
|
121
|
+
end
|
122
|
+
|
123
|
+
def select_field(name, **options)
|
124
|
+
field(name, field_type: :select, **options)
|
125
|
+
end
|
126
|
+
|
127
|
+
def has_many_field(name, **options)
|
128
|
+
field(name, field_type: :has_many, **options)
|
129
|
+
end
|
130
|
+
|
131
|
+
def date_field(name, **options)
|
132
|
+
field(name, field_type: :date, **options)
|
133
|
+
end
|
134
|
+
|
135
|
+
def datetime_field(name, **options)
|
136
|
+
field(name, field_type: :datetime, **options)
|
137
|
+
end
|
138
|
+
|
139
|
+
def number_field(name, **options)
|
140
|
+
field(name, field_type: :number, **options)
|
141
|
+
end
|
142
|
+
|
143
|
+
def password_field(name, **options)
|
144
|
+
field(name, field_type: :password, **options)
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def required_field?(name)
|
150
|
+
# Check if field is required based on model validations
|
151
|
+
# This would need to be implemented based on your validation setup
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
def readonly_field?(name)
|
156
|
+
# Check if field should be readonly
|
157
|
+
# This would need to be implemented based on your configuration
|
158
|
+
false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Builder for form actions
|
163
|
+
class FormActionsBuilder
|
164
|
+
def initialize(parent_builder)
|
165
|
+
@parent_builder = parent_builder
|
166
|
+
end
|
167
|
+
|
168
|
+
def submit(text = "Save", **attributes)
|
169
|
+
submit_node = Nodes::FormAction.new(
|
170
|
+
:submit,
|
171
|
+
text: text,
|
172
|
+
**attributes
|
173
|
+
)
|
174
|
+
@parent_builder.add_node(submit_node)
|
175
|
+
end
|
176
|
+
|
177
|
+
def cancel(text = "Cancel", href: :back, **attributes)
|
178
|
+
cancel_node = Nodes::FormAction.new(
|
179
|
+
:cancel,
|
180
|
+
text: text,
|
181
|
+
href: href,
|
182
|
+
**attributes
|
183
|
+
)
|
184
|
+
@parent_builder.add_node(cancel_node)
|
185
|
+
end
|
186
|
+
|
187
|
+
def reset(text = "Reset", **attributes)
|
188
|
+
reset_node = Nodes::FormAction.new(
|
189
|
+
:reset,
|
190
|
+
text: text,
|
191
|
+
**attributes
|
192
|
+
)
|
193
|
+
@parent_builder.add_node(reset_node)
|
194
|
+
end
|
195
|
+
|
196
|
+
def button(text, action:, **attributes)
|
197
|
+
button_node = Nodes::FormAction.new(
|
198
|
+
:button,
|
199
|
+
text: text,
|
200
|
+
action: action,
|
201
|
+
**attributes
|
202
|
+
)
|
203
|
+
@parent_builder.add_node(button_node)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
module Layouts
|
3
|
+
module Builders
|
4
|
+
# IndexLayoutBuilder for constructing index page layouts
|
5
|
+
# Specialized builder for index table field configurations
|
6
|
+
class IndexLayoutBuilder < BaseLayoutBuilder
|
7
|
+
def initialize(resource_class: nil)
|
8
|
+
super(:index, resource_class: resource_class)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Simple field method for index - only accepts field name
|
12
|
+
# Configuration is extracted from already registered resource fields
|
13
|
+
def field(name, **options)
|
14
|
+
# Create a simple field node with just the name
|
15
|
+
# The actual field configuration will be looked up from resource fields_config
|
16
|
+
field_node = Nodes::FieldNode.new(name, options)
|
17
|
+
add_node(field_node)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
module Layouts
|
3
|
+
module Builders
|
4
|
+
# Builder specifically for show page layouts
|
5
|
+
class ShowLayoutBuilder < BaseLayoutBuilder
|
6
|
+
def initialize(resource_class: nil)
|
7
|
+
super(:show, resource_class: resource_class)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Show-specific DSL methods
|
11
|
+
|
12
|
+
# Add a description list for key-value pairs
|
13
|
+
def description_list(**attributes, &block)
|
14
|
+
dl_node = Nodes::DescriptionList.new(attributes)
|
15
|
+
|
16
|
+
if block_given?
|
17
|
+
with_container(dl_node, &block)
|
18
|
+
else
|
19
|
+
add_node(dl_node)
|
20
|
+
end
|
21
|
+
|
22
|
+
dl_node
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add a card component
|
26
|
+
def card(title: nil, **attributes, &block)
|
27
|
+
card_node = Nodes::Card.new(title: title, **attributes)
|
28
|
+
|
29
|
+
if block_given?
|
30
|
+
with_container(card_node, &block)
|
31
|
+
else
|
32
|
+
add_node(card_node)
|
33
|
+
end
|
34
|
+
|
35
|
+
card_node
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add a metric card
|
39
|
+
def metric(label:, value:, **attributes)
|
40
|
+
metric_node = Nodes::MetricCard.new(
|
41
|
+
label: label,
|
42
|
+
value: value,
|
43
|
+
**attributes
|
44
|
+
)
|
45
|
+
add_node(metric_node)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add a metric card (accepts both label and title)
|
49
|
+
def metric_card(title: nil, label: nil, value: nil, **attributes)
|
50
|
+
metric_node = Nodes::MetricCard.new(
|
51
|
+
title: title,
|
52
|
+
label: label,
|
53
|
+
value: value,
|
54
|
+
**attributes
|
55
|
+
)
|
56
|
+
add_node(metric_node)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Add an action bar
|
60
|
+
def action_bar(**attributes, &block)
|
61
|
+
action_bar_node = Nodes::ActionBar.new(attributes)
|
62
|
+
add_node(action_bar_node)
|
63
|
+
|
64
|
+
if block_given?
|
65
|
+
@current_container_stack.push(action_bar_node)
|
66
|
+
action_builder = ActionBarBuilder.new(self, action_bar_node)
|
67
|
+
action_builder.instance_exec(&block)
|
68
|
+
@current_container_stack.pop
|
69
|
+
end
|
70
|
+
|
71
|
+
action_bar_node
|
72
|
+
end
|
73
|
+
|
74
|
+
# Add a panel (collapsible section)
|
75
|
+
def panel(title, expanded: true, **attributes, &block)
|
76
|
+
panel_node = Nodes::Panel.new(
|
77
|
+
title,
|
78
|
+
attributes.merge(expanded: expanded)
|
79
|
+
)
|
80
|
+
|
81
|
+
if block_given?
|
82
|
+
with_container(panel_node, &block)
|
83
|
+
else
|
84
|
+
add_node(panel_node)
|
85
|
+
end
|
86
|
+
|
87
|
+
panel_node
|
88
|
+
end
|
89
|
+
|
90
|
+
# Add badge
|
91
|
+
def badge(text, variant: :default, **attributes)
|
92
|
+
badge_node = Nodes::Badge.new(
|
93
|
+
text,
|
94
|
+
attributes.merge(variant: variant)
|
95
|
+
)
|
96
|
+
add_node(badge_node)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Add related resources section
|
100
|
+
def related(resource_name, **attributes, &block)
|
101
|
+
related_node = Nodes::RelatedResources.new(
|
102
|
+
resource_name,
|
103
|
+
attributes
|
104
|
+
)
|
105
|
+
|
106
|
+
if block_given?
|
107
|
+
with_container(related_node, &block)
|
108
|
+
else
|
109
|
+
add_node(related_node)
|
110
|
+
end
|
111
|
+
|
112
|
+
related_node
|
113
|
+
end
|
114
|
+
|
115
|
+
# Add row layout
|
116
|
+
def row(**attributes, &block)
|
117
|
+
row_node = Nodes::Row.new(attributes)
|
118
|
+
|
119
|
+
if block_given?
|
120
|
+
with_container(row_node, &block)
|
121
|
+
else
|
122
|
+
add_node(row_node)
|
123
|
+
end
|
124
|
+
|
125
|
+
row_node
|
126
|
+
end
|
127
|
+
|
128
|
+
# Add column layout
|
129
|
+
def column(**attributes, &block)
|
130
|
+
column_node = Nodes::Column.new(attributes)
|
131
|
+
|
132
|
+
if block_given?
|
133
|
+
with_container(column_node, &block)
|
134
|
+
else
|
135
|
+
add_node(column_node)
|
136
|
+
end
|
137
|
+
|
138
|
+
column_node
|
139
|
+
end
|
140
|
+
|
141
|
+
# Add heading
|
142
|
+
def heading(text, level: 2, **attributes)
|
143
|
+
heading_node = Nodes::Heading.new(text, attributes.merge(level: level))
|
144
|
+
add_node(heading_node)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Add content block
|
148
|
+
def content(&block)
|
149
|
+
content_node = Nodes::Content.new(block: block)
|
150
|
+
add_node(content_node)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Builder for action bars
|
155
|
+
class ActionBarBuilder
|
156
|
+
def initialize(parent_builder, action_bar_node)
|
157
|
+
@parent_builder = parent_builder
|
158
|
+
@action_bar_node = action_bar_node
|
159
|
+
end
|
160
|
+
|
161
|
+
def link(text, href:, **attributes)
|
162
|
+
action_node = Nodes::Action.new(
|
163
|
+
:link,
|
164
|
+
text: text,
|
165
|
+
href: href,
|
166
|
+
**attributes
|
167
|
+
)
|
168
|
+
@action_bar_node.add_child(action_node)
|
169
|
+
end
|
170
|
+
|
171
|
+
def button(text, action:, **attributes)
|
172
|
+
action_node = Nodes::Action.new(
|
173
|
+
:button,
|
174
|
+
text: text,
|
175
|
+
action: action,
|
176
|
+
**attributes
|
177
|
+
)
|
178
|
+
@action_bar_node.add_child(action_node)
|
179
|
+
end
|
180
|
+
|
181
|
+
def dropdown(text, **attributes, &block)
|
182
|
+
dropdown_node = Nodes::Dropdown.new(
|
183
|
+
text: text,
|
184
|
+
**attributes
|
185
|
+
)
|
186
|
+
@action_bar_node.add_child(dropdown_node)
|
187
|
+
|
188
|
+
if block_given?
|
189
|
+
@parent_builder.current_container_stack.push(dropdown_node)
|
190
|
+
@parent_builder.instance_exec(&block)
|
191
|
+
@parent_builder.current_container_stack.pop
|
192
|
+
end
|
193
|
+
|
194
|
+
dropdown_node
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
module Layouts
|
3
|
+
# DSL module to be included in Resource classes
|
4
|
+
module DSL
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :show_layout_definition, default: nil
|
9
|
+
class_attribute :form_layout_definition, default: nil
|
10
|
+
class_attribute :index_layout_definition, default: nil
|
11
|
+
end
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
# Define show page layout
|
15
|
+
def show(component_or_block = nil, &block)
|
16
|
+
if component_or_block.is_a?(Class)
|
17
|
+
# Direct component mode
|
18
|
+
self.show_layout_definition = [:component, component_or_block]
|
19
|
+
elsif block_given?
|
20
|
+
# DSL mode - build AST
|
21
|
+
builder = Builders::ShowLayoutBuilder.new(resource_class: self)
|
22
|
+
builder.instance_exec(&block)
|
23
|
+
self.show_layout_definition = [:ast, builder.build]
|
24
|
+
else
|
25
|
+
# Return current definition
|
26
|
+
self.show_layout_definition
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Define form page layout
|
31
|
+
def form(component_or_block = nil, &block)
|
32
|
+
if component_or_block.is_a?(Class)
|
33
|
+
# Direct component mode
|
34
|
+
self.form_layout_definition = [:component, component_or_block]
|
35
|
+
elsif block_given?
|
36
|
+
# DSL mode - build AST
|
37
|
+
builder = Builders::FormLayoutBuilder.new(resource_class: self)
|
38
|
+
builder.instance_exec(&block)
|
39
|
+
self.form_layout_definition = [:ast, builder.build]
|
40
|
+
else
|
41
|
+
# Return current definition
|
42
|
+
self.form_layout_definition
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Define index page layout
|
47
|
+
def index(component_or_block = nil, &block)
|
48
|
+
if component_or_block.is_a?(Class)
|
49
|
+
# Direct component mode
|
50
|
+
self.index_layout_definition = [:component, component_or_block]
|
51
|
+
elsif block_given?
|
52
|
+
# DSL mode - build AST
|
53
|
+
builder = Builders::IndexLayoutBuilder.new(resource_class: self)
|
54
|
+
builder.instance_exec(&block)
|
55
|
+
self.index_layout_definition = [:ast, builder.build]
|
56
|
+
else
|
57
|
+
# Return current definition
|
58
|
+
self.index_layout_definition
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check if custom show layout is defined
|
63
|
+
def has_custom_show_layout?
|
64
|
+
!show_layout_definition.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if custom form layout is defined
|
68
|
+
def has_custom_form_layout?
|
69
|
+
!form_layout_definition.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Check if custom index layout is defined
|
73
|
+
def has_custom_index_layout?
|
74
|
+
!index_layout_definition.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check if form layout contains tabs
|
78
|
+
def has_form_tabs?
|
79
|
+
return false unless has_custom_form_layout?
|
80
|
+
|
81
|
+
# Build the form layout to check if it contains tabs
|
82
|
+
begin
|
83
|
+
builder = Builders::FormLayoutBuilder.new(resource_class: self)
|
84
|
+
builder.instance_exec(&form_layout_content)
|
85
|
+
root_node = builder.build
|
86
|
+
|
87
|
+
# Check if the AST contains any tabs nodes
|
88
|
+
contains_tabs_node?(root_node)
|
89
|
+
rescue => e
|
90
|
+
false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Get show layout type
|
95
|
+
def show_layout_type
|
96
|
+
return nil unless show_layout_definition
|
97
|
+
show_layout_definition[0]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Get form layout type
|
101
|
+
def form_layout_type
|
102
|
+
return nil unless form_layout_definition
|
103
|
+
form_layout_definition[0]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get show layout content
|
107
|
+
def show_layout_content
|
108
|
+
return nil unless show_layout_definition
|
109
|
+
show_layout_definition[1]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get form layout content
|
113
|
+
def form_layout_content
|
114
|
+
return nil unless form_layout_definition
|
115
|
+
form_layout_definition[1]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get index layout type
|
119
|
+
def index_layout_type
|
120
|
+
return nil unless index_layout_definition
|
121
|
+
index_layout_definition[0]
|
122
|
+
end
|
123
|
+
|
124
|
+
# Get index layout content
|
125
|
+
def index_layout_content
|
126
|
+
return nil unless index_layout_definition
|
127
|
+
index_layout_definition[1]
|
128
|
+
end
|
129
|
+
|
130
|
+
# Reset layouts
|
131
|
+
def reset_layouts!
|
132
|
+
self.show_layout_definition = nil
|
133
|
+
self.form_layout_definition = nil
|
134
|
+
self.index_layout_definition = nil
|
135
|
+
end
|
136
|
+
|
137
|
+
# Generate default show layout from fields
|
138
|
+
def generate_default_show_layout
|
139
|
+
builder = Builders::ShowLayoutBuilder.new(resource_class: self)
|
140
|
+
|
141
|
+
# Group fields by category or just show all
|
142
|
+
if fields_config.any?
|
143
|
+
builder.card(title: resource_title) do
|
144
|
+
builder.grid(columns: 2) do
|
145
|
+
fields_config.each do |field_config|
|
146
|
+
next if field_config[:show] == false
|
147
|
+
builder.field(field_config[:name], **field_config.except(:name))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
[:ast, builder.build]
|
154
|
+
end
|
155
|
+
|
156
|
+
# Generate default form layout from fields
|
157
|
+
def generate_default_form_layout
|
158
|
+
builder = Builders::FormLayoutBuilder.new(resource_class: self)
|
159
|
+
|
160
|
+
if fields_config.any?
|
161
|
+
# Group editable fields
|
162
|
+
editable_fields = fields_config.reject { |f| f[:readonly] || f[:form] == false }
|
163
|
+
|
164
|
+
if editable_fields.any?
|
165
|
+
builder.section("Details") do
|
166
|
+
editable_fields.each do |field_config|
|
167
|
+
builder.field(field_config[:name], **field_config.except(:name))
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
builder.form_actions do
|
173
|
+
builder.submit
|
174
|
+
builder.cancel
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
[:ast, builder.build]
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
# Recursively check if AST contains tabs nodes
|
184
|
+
def contains_tabs_node?(node)
|
185
|
+
return false unless node
|
186
|
+
|
187
|
+
# Check if current node is a tabs node
|
188
|
+
return true if node.is_a?(EasyAdmin::Layouts::Nodes::Tabs)
|
189
|
+
|
190
|
+
# Check children recursively
|
191
|
+
if node.respond_to?(:children) && node.children
|
192
|
+
node.children.any? { |child| contains_tabs_node?(child) }
|
193
|
+
else
|
194
|
+
false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|