avo 2.9.1.pre5 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -5
  3. data/README.md +4 -0
  4. data/app/assets/stylesheets/css/buttons.css +4 -1
  5. data/app/components/avo/actions_component.rb +6 -2
  6. data/app/components/avo/base_component.rb +2 -0
  7. data/app/components/avo/button_component.rb +3 -1
  8. data/app/components/avo/fields/common/key_value_component.html.erb +2 -2
  9. data/app/components/avo/fields/common/single_file_viewer_component.rb +1 -1
  10. data/app/components/avo/fields/edit_component.rb +5 -0
  11. data/app/components/avo/fields/show_component.rb +1 -1
  12. data/app/components/avo/index/ordering/button_component.rb +2 -12
  13. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  14. data/app/components/avo/index/resource_controls_component.rb +5 -1
  15. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  16. data/app/components/avo/index/table_row_component.html.erb +1 -1
  17. data/app/components/avo/item_switcher_component.html.erb +19 -0
  18. data/app/components/avo/item_switcher_component.rb +45 -0
  19. data/app/components/avo/panel_component.html.erb +23 -24
  20. data/app/components/avo/panel_component.rb +8 -5
  21. data/app/components/avo/tab_group_component.html.erb +53 -0
  22. data/app/components/avo/tab_group_component.rb +51 -0
  23. data/app/components/avo/tab_switcher_component.html.erb +21 -0
  24. data/app/components/avo/tab_switcher_component.rb +86 -0
  25. data/app/components/avo/views/resource_edit_component.html.erb +34 -56
  26. data/app/components/avo/views/resource_edit_component.rb +10 -0
  27. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  28. data/app/components/avo/views/resource_index_component.rb +3 -3
  29. data/app/components/avo/views/resource_show_component.html.erb +58 -89
  30. data/app/components/avo/views/resource_show_component.rb +2 -2
  31. data/app/controllers/avo/actions_controller.rb +1 -1
  32. data/app/controllers/avo/application_controller.rb +20 -3
  33. data/app/helpers/avo/application_helper.rb +0 -6
  34. data/app/helpers/avo/url_helpers.rb +1 -1
  35. data/app/javascript/avo.js +5 -1
  36. data/app/javascript/js/controllers/loading_button_controller.js +25 -21
  37. data/app/javascript/js/controllers/tabs_controller.js +86 -0
  38. data/app/javascript/js/controllers.js +2 -0
  39. data/app/views/avo/base/index.html.erb +1 -1
  40. data/app/views/avo/base/show.html.erb +1 -1
  41. data/app/views/avo/cards/show.html.erb +1 -1
  42. data/app/views/avo/debug/index.html.erb +1 -1
  43. data/app/views/avo/home/_actions.html.erb +1 -1
  44. data/app/views/avo/home/_dashboards.html.erb +19 -0
  45. data/app/views/avo/home/_filters.html.erb +1 -1
  46. data/app/views/avo/home/_resources.html.erb +1 -1
  47. data/app/views/avo/home/failed_to_load.html.erb +1 -1
  48. data/app/views/avo/home/index.html.erb +14 -2
  49. data/app/views/avo/partials/_javascript.html.erb +1 -1
  50. data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
  51. data/app/views/avo/private/design.html.erb +1 -1
  52. data/config/routes.rb +1 -1
  53. data/lib/avo/app.rb +9 -2
  54. data/lib/avo/base_action.rb +2 -19
  55. data/lib/avo/base_card.rb +1 -7
  56. data/lib/avo/base_resource.rb +0 -94
  57. data/lib/avo/base_resource_tool.rb +3 -1
  58. data/lib/avo/concerns/has_fields.rb +247 -50
  59. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  60. data/lib/avo/concerns/is_resource_item.rb +36 -0
  61. data/lib/avo/dashboards/base_dashboard.rb +1 -1
  62. data/lib/avo/dsl/field_parser.rb +83 -0
  63. data/lib/avo/fields/base_field.rb +19 -2
  64. data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
  65. data/lib/avo/fields/has_base_field.rb +20 -1
  66. data/lib/avo/fields/has_one_field.rb +4 -1
  67. data/lib/avo/grid_collector.rb +6 -3
  68. data/lib/avo/items_holder.rb +68 -0
  69. data/lib/avo/licensing/h_q.rb +10 -0
  70. data/lib/avo/main_panel.rb +3 -0
  71. data/lib/avo/menu/builder.rb +7 -7
  72. data/lib/avo/panel.rb +25 -0
  73. data/lib/avo/panel_builder.rb +23 -0
  74. data/lib/avo/services/uri_service.rb +71 -0
  75. data/lib/avo/tab.rb +78 -0
  76. data/lib/avo/tab_builder.rb +25 -0
  77. data/lib/avo/tab_group.rb +40 -0
  78. data/lib/avo/tab_group_builder.rb +43 -0
  79. data/lib/avo/version.rb +1 -1
  80. data/lib/avo.rb +1 -0
  81. data/lib/generators/avo/templates/resource/controller.tt +2 -0
  82. data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
  83. data/lib/generators/avo/templates/tool/view.tt +1 -1
  84. data/public/avo-assets/avo.css +27 -3
  85. data/public/avo-assets/avo.js +73 -73
  86. data/public/avo-assets/avo.js.map +3 -3
  87. metadata +24 -14
  88. data/app/assets/builds/action_cable.js +0 -2
  89. data/app/assets/builds/action_cable.js.map +0 -7
  90. data/app/assets/builds/application.js +0 -2
  91. data/app/assets/builds/application.js.map +0 -7
  92. data/app/assets/builds/avo.css +0 -9028
  93. data/app/assets/builds/avo.js +0 -512
  94. data/app/assets/builds/avo.js.map +0 -7
  95. data/app/assets/builds/avo_custom.js +0 -6
  96. data/app/assets/builds/avo_custom.js.map +0 -7
  97. data/lib/avo/concerns/has_tools.rb +0 -47
@@ -4,88 +4,285 @@ module Avo
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_attribute :fields
8
- class_attribute :fields_index, default: 0
7
+ class_attribute :items_holder
8
+ class_attribute :items_index, default: 0
9
+ class_attribute :tabs_holder
10
+ class_attribute :tabs_tabs_holder
11
+ class_attribute :raw_tabs
12
+ class_attribute :tools_holder
9
13
  end
10
14
 
11
15
  class_methods do
12
- def field(field_name, as:, **args, &block)
13
- self.invalid_fields ||= []
16
+ delegate :tool, to: :items_holder
14
17
 
15
- field_instance = parse_field(field_name, as: as, order_index: fields_index, **args, &block)
18
+ # DSL methods
19
+ def field(name, **args, &block)
20
+ ensure_items_holder_initialized
16
21
 
17
- if field_instance.present?
18
- add_to_fields field_instance
22
+ self.items_holder.field name, **args, &block
23
+ end
24
+
25
+ def panel(name = nil, **args, &block)
26
+ ensure_items_holder_initialized
27
+
28
+ self.items_holder.panel name, **args, &block
29
+ end
30
+
31
+ def tabs(&block)
32
+ ensure_items_holder_initialized
33
+
34
+ self.items_holder.tabs Avo::TabGroupBuilder.parse_block(&block)
35
+ end
36
+
37
+ def heading(body, **args)
38
+ self.items_holder.add_item Avo::Fields::HeadingField.new(body, order_index: items_index, **args)
39
+ end
40
+ # END DSL methods
41
+
42
+ def items
43
+ if self.items_holder.present?
44
+ self.items_holder.items
19
45
  else
20
- self.invalid_fields << ({
21
- name: field_name,
22
- as: as,
23
- resource: name,
24
- message: "There's an invalid field configuration for this resource. <br/> <code class='px-1 py-px rounded bg-red-600'>field :#{field_name}, as: #{as}</code>"
25
- })
46
+ []
26
47
  end
27
48
  end
28
49
 
29
- def parse_field(field_name, as:, **args, &block)
30
- # The field is passed as a symbol eg: :text, :color_picker, :trix
31
- if as.is_a? Symbol
32
- parse_symbol field_name, as: as, **args, &block
33
- elsif as.is_a? Class
34
- parse_class field_name, as: as, **args, &block
50
+ def tools
51
+ self.tools_holder
52
+ end
53
+
54
+ # Dives deep into panels and tabs to fetch all the fields for a resource.
55
+ def fields(only_root: false)
56
+ fields = []
57
+
58
+ self.items.each do |item|
59
+ next if item.nil?
60
+
61
+ unless only_root
62
+ # Dive into panels to fetch their fields
63
+ if item.is_panel?
64
+ fields << extract_fields_from_items(item)
65
+ end
66
+
67
+ # Dive into tabs to fetch their fields
68
+ if item.is_tab_group?
69
+ item.items.map do |tab|
70
+ fields << extract_fields_from_items(tab)
71
+ end
72
+ end
73
+ end
74
+
75
+
76
+ if item.is_field?
77
+ fields << item
78
+ end
35
79
  end
80
+
81
+ fields.flatten
36
82
  end
37
83
 
38
- def heading(body, **args)
39
- add_to_fields Avo::Fields::HeadingField.new(body, order_index: fields_index, **args)
84
+ def tab_groups
85
+ self.items.select do |item|
86
+ item.instance_of? Avo::TabGroup
87
+ end
40
88
  end
41
89
 
42
90
  private
43
91
 
92
+ def extract_fields_from_items(thing)
93
+ fields = []
94
+
95
+ thing.items.each do |item|
96
+ if item.is_field?
97
+ fields << item
98
+ elsif item.is_panel?
99
+ fields << extract_fields_from_items(item)
100
+ end
101
+ end
102
+
103
+ fields
104
+ end
105
+
106
+ def ensure_items_holder_initialized
107
+ self.items_holder ||= Avo::ItemsHolder.new
108
+ end
109
+
44
110
  def add_to_fields(instance)
45
- self.fields ||= []
46
- self.fields << instance
47
- self.fields_index += 1
111
+ self.items ||= []
112
+ self.items << instance
113
+ increment_order_index
48
114
  end
49
115
 
50
- def instantiate_field(field_name, klass:, **args, &block)
51
- if block
52
- klass.new(field_name, **args || {}, &block)
53
- else
54
- klass.new(field_name, **args || {})
55
- end
116
+ def increment_order_index
117
+ self.items_index += 1
118
+ end
119
+ end
120
+
121
+ delegate :invalid_fields, to: :items_holder
122
+ delegate :items, to: :items_holder
123
+
124
+ def hydrate_fields(model: nil, view: nil)
125
+ fields.map do |field|
126
+ field.hydrate(model: @model, view: @view, resource: self)
127
+ end
128
+
129
+ self
130
+ end
131
+
132
+ def get_tabs
133
+ tabs_holder
134
+ end
135
+
136
+ def fields(**args)
137
+ self.class.fields(**args)
138
+ end
139
+
140
+ def tab_groups
141
+ self.class.tab_groups
142
+ end
143
+
144
+ def get_field_definitions(only_root: false)
145
+ fields = self.fields(only_root: only_root)
146
+
147
+ return [] if fields.blank?
148
+
149
+ items = fields.map do |field|
150
+ field.hydrate(resource: self, user: user, view: view)
56
151
  end
57
152
 
58
- def field_class_from_symbol(symbol)
59
- matched_field = Avo::App.fields.find do |field|
60
- field[:name].to_s == symbol.to_s
153
+ if Avo::App.license.lacks_with_trial(:custom_fields)
154
+ items = items.reject do |field|
155
+ field.custom?
61
156
  end
157
+ end
62
158
 
63
- return matched_field[:class] if matched_field.present? && matched_field[:class].present?
159
+ if Avo::App.license.lacks_with_trial(:advanced_fields)
160
+ items = items.reject do |field|
161
+ field.type == "tags"
162
+ end
64
163
  end
65
164
 
66
- def parse_symbol(field_name, as:, **args, &block)
67
- field_class = field_class_from_symbol(as)
165
+ items
166
+ end
68
167
 
69
- if field_class.present?
70
- # The field has been registered before.
71
- instantiate_field(field_name, klass: field_class, **args, &block)
72
- else
73
- # The symbol can be transformed to a class and found.
74
- class_name = as.to_s.camelize
75
- field_class = "#{class_name}Field"
168
+ def get_fields(panel: nil, reflection: nil, only_root: false)
169
+ fields = get_field_definitions(only_root: only_root)
170
+ .select do |field|
171
+ field.visible_on?(view)
172
+ end
173
+ .select do |field|
174
+ field.visible?
175
+ end
176
+ .select do |field|
177
+ is_valid = true
178
+
179
+ # Strip out the reflection field in index queries with a parent association.
180
+ if reflection.present?
181
+ # regular non-polymorphic association
182
+ # we're matching the reflection inverse_of foriegn key with the field's foreign_key
183
+ if field.is_a?(Avo::Fields::BelongsToField)
184
+ if field.respond_to?(:foreign_key) &&
185
+ reflection.inverse_of.present? &&
186
+ reflection.inverse_of.foreign_key == field.foreign_key
187
+ is_valid = false
188
+ end
76
189
 
77
- # Discover & load custom field classes
78
- if Object.const_defined? field_class
79
- instantiate_field(field_name, klass: field_class.safe_constantize, **args, &block)
190
+ # polymorphic association
191
+ if field.respond_to?(:foreign_key) &&
192
+ field.is_polymorphic? &&
193
+ reflection.respond_to?(:polymorphic?) &&
194
+ reflection.inverse_of.foreign_key == field.reflection.foreign_key
195
+ is_valid = false
196
+ end
197
+ end
80
198
  end
199
+
200
+ is_valid
201
+ end
202
+
203
+ if panel.present?
204
+ fields = fields.select do |field|
205
+ field.panel_name == panel
81
206
  end
82
207
  end
83
208
 
84
- def parse_class(field_name, as:, **args, &block)
85
- # The field has been passed as a class.
86
- if Object.const_defined? as.to_s
87
- instantiate_field(field_name, klass: as, **args, &block)
209
+ hydrate_fields(model: @model, view: @view)
210
+
211
+ fields
212
+ end
213
+
214
+ def get_field(id)
215
+ get_field_definitions.find do |f|
216
+ f.id == id.to_sym
217
+ end
218
+ end
219
+
220
+ def tools
221
+ check_license
222
+
223
+ return [] if App.license.lacks_with_trial :resource_tools
224
+ return [] if self.class.tools.blank?
225
+
226
+ self.items
227
+ .select do |item|
228
+ next if item.nil?
229
+
230
+ item.is_tool?
88
231
  end
232
+ .map do |tool|
233
+ tool.hydrate view: view
234
+ tool
235
+ end
236
+ .select do |item|
237
+ item.visible_on?(view)
238
+ end
239
+ end
240
+
241
+ # Separates the fields that are in a panel and those that are just hanging out.
242
+ # Take the ones that aren't placed into a panel and add them to the "default" panel.
243
+ # This is to keep compatibility with the versions before 2.10 when you didn't have the ability to add fields to panels.
244
+ def get_items
245
+ panelless_items = []
246
+ panelfull_items = []
247
+
248
+ items.each do |item|
249
+ # fields and tabs can be hidden on some views
250
+ if item.respond_to? :visible_on?
251
+ next unless item.visible_on?(view)
252
+ end
253
+ if item.respond_to? :visible?
254
+ next unless item.visible?
255
+ end
256
+
257
+ if item.is_field?
258
+ if item.has_own_panel?
259
+ panelfull_items << item
260
+ else
261
+ panelless_items << item
262
+ end
263
+ else
264
+ panelfull_items << item
265
+ end
266
+ end
267
+
268
+ # Add all the panelles fields to a new panel
269
+ main_panel_holder = Avo::ItemsHolder.new
270
+ main_panel_holder.items = panelless_items
271
+
272
+ # Add that panel to the main panel
273
+ main_panel = Avo::MainPanel.new
274
+ main_panel.items_holder = main_panel_holder
275
+
276
+ # Return all the items but this time with all the panelless ones inside the main panel
277
+ [main_panel, *panelfull_items]
278
+ end
279
+
280
+ private
281
+
282
+ def check_license
283
+ if !Rails.env.production? && App.license.present? && App.license.lacks(:resource_tools)
284
+ # Add error message to let the developer know the resource tool will not be available in a production environment.
285
+ Avo::App.error_messages.push "Warning: Your license is invalid or doesn't support resource tools. The resource tools will not be visible in a production environment."
89
286
  end
90
287
  end
91
288
  end
@@ -50,7 +50,7 @@ module Avo
50
50
  end
51
51
 
52
52
  def add_default_data_attributes(attributes, name, element, view)
53
- if !attributes.nil? && name == :data && element == :input && view.in?([:edit, :new]) && resource.present?
53
+ if !attributes.nil? && name == :data && element == :input && view.in?([:edit, :new]) && resource.present? && resource.respond_to?(:get_stimulus_controllers)
54
54
  extra_attributes = resource.get_stimulus_controllers
55
55
  .split(" ")
56
56
  .map do |controller|
@@ -0,0 +1,36 @@
1
+ # This concern helps us figure out what kind of items (field, tool, tab_group, or panel) have been passed to the resource or action.
2
+ module Avo
3
+ module Concerns
4
+ module IsResourceItem
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :item_type, default: nil
9
+ end
10
+
11
+ def is_field?
12
+ self.class.item_type == :field
13
+ end
14
+
15
+ def is_panel?
16
+ self.class.item_type == :panel || self.class.item_type == :main_panel
17
+ end
18
+
19
+ def is_main_panel?
20
+ self.class.item_type == :main_panel
21
+ end
22
+
23
+ def is_tool?
24
+ self.class.item_type == :tool
25
+ end
26
+
27
+ def is_tab?
28
+ self.class.item_type == :tab
29
+ end
30
+
31
+ def is_tab_group?
32
+ self.class.item_type == :tab_group
33
+ end
34
+ end
35
+ end
36
+ end
@@ -65,7 +65,7 @@ module Avo
65
65
  end
66
66
 
67
67
  def navigation_path
68
- "#{Avo::App.root_path}/dashboards/#{id}"
68
+ Avo::App.view_context.avo.dashboard_path id
69
69
  end
70
70
 
71
71
  def is_visible?
@@ -0,0 +1,83 @@
1
+ module Avo
2
+ module Dsl
3
+ class FieldParser
4
+ attr_reader :as
5
+ attr_reader :args
6
+ attr_reader :id
7
+ attr_reader :block
8
+ attr_reader :instance
9
+ attr_reader :order_index
10
+
11
+ def initialize(id:, order_index: 0, **args, &block)
12
+ @id = id
13
+ @as = args.fetch(:as, nil)
14
+ @order_index = order_index
15
+ @args = args
16
+ @block = block
17
+ @instance = nil
18
+ end
19
+
20
+ def valid?
21
+ instance.present?
22
+ end
23
+
24
+ def invalid?
25
+ !valid?
26
+ end
27
+
28
+ def parse
29
+ # The field is passed as a symbol eg: :text, :color_picker, :trix
30
+ @instance = if as.is_a? Symbol
31
+ parse_from_symbol
32
+ elsif as.is_a? Class
33
+ parse_from_class
34
+ end
35
+
36
+ self
37
+ end
38
+
39
+ private
40
+
41
+ def parse_from_symbol
42
+ field_class = field_class_from_symbol(as)
43
+
44
+ if field_class.present?
45
+ # The field has been registered before.
46
+ instantiate_field(id, klass: field_class, **args, &block)
47
+ else
48
+ # The symbol can be transformed to a class and found.
49
+ class_name = as.to_s.camelize
50
+ field_class = "#{class_name}Field"
51
+
52
+ # Discover & load custom field classes
53
+ if Object.const_defined? field_class
54
+ instantiate_field(id, klass: field_class.safe_constantize, **args, &block)
55
+ end
56
+ end
57
+ end
58
+
59
+ def parse_from_class
60
+ # The field has been passed as a class.
61
+ if Object.const_defined? as.to_s
62
+ instantiate_field(id, klass: as, **args, &block)
63
+ end
64
+ end
65
+
66
+ def instantiate_field(id, klass:, **args, &block)
67
+ if block
68
+ klass.new(id, **args || {}, &block)
69
+ else
70
+ klass.new(id, **args || {})
71
+ end
72
+ end
73
+
74
+ def field_class_from_symbol(symbol)
75
+ matched_field = Avo::App.fields.find do |field|
76
+ field[:name].to_s == symbol.to_s
77
+ end
78
+
79
+ return matched_field[:class] if matched_field.present? && matched_field[:class].present?
80
+ end
81
+ end
82
+ end
83
+ end
@@ -4,8 +4,11 @@ module Avo
4
4
  extend ActiveSupport::DescendantsTracker
5
5
  extend Avo::Fields::FieldExtensions::HasFieldName
6
6
 
7
- include Avo::Fields::FieldExtensions::VisibleInDifferentViews
7
+ include Avo::Concerns::IsResourceItem
8
+ include Avo::Concerns::HandlesFieldArgs
9
+
8
10
  include ActionView::Helpers::UrlHelper
11
+ include Avo::Fields::FieldExtensions::VisibleInDifferentViews
9
12
 
10
13
  include Avo::Concerns::HandlesFieldArgs
11
14
  include Avo::Concerns::HasHTMLAttributes
@@ -48,8 +51,9 @@ module Avo
48
51
  attr_reader :panel_name
49
52
 
50
53
  class_attribute :field_name_attribute
54
+ class_attribute :item_type, default: :field
51
55
 
52
- def initialize(id, _options: {}, **args, &block)
56
+ def initialize(id, **args, &block)
53
57
  super(id, **args, &block)
54
58
 
55
59
  @id = id
@@ -206,9 +210,14 @@ module Avo
206
210
 
207
211
  # Try and build the component class or fallback to a blank one
208
212
  def component_for_view(view = :index)
213
+ # Use the edit variant for all "update" views
214
+ view = :edit if view.in? [:new, :create, :update]
215
+
209
216
  component_class = "::Avo::Fields::#{view_component_name}::#{view.to_s.camelize}Component"
210
217
  component_class.constantize
211
218
  rescue
219
+ # When returning nil, a race condition happens and throws an error in some environments.
220
+ # See https://github.com/avo-hq/avo/pull/365
212
221
  ::Avo::BlankFieldComponent
213
222
  end
214
223
 
@@ -228,6 +237,14 @@ module Avo
228
237
  true
229
238
  end
230
239
 
240
+ def visible_in_reflection?
241
+ true
242
+ end
243
+
244
+ def hidden_in_reflection?
245
+ !visible_in_reflection?
246
+ end
247
+
231
248
  private
232
249
 
233
250
  def model_or_class(model)
@@ -7,13 +7,20 @@ module Avo
7
7
  attr_accessor :show_on_new
8
8
  attr_accessor :show_on_edit
9
9
 
10
- def initialize(id, **args, &block)
10
+ def initialize(id = nil, **args, &block)
11
11
  @show_on_index = @show_on_index.nil? ? true : @show_on_index
12
12
  @show_on_show = @show_on_show.nil? ? true : @show_on_show
13
13
  @show_on_new = @show_on_new.nil? ? true : @show_on_new
14
14
  @show_on_edit = @show_on_edit.nil? ? true : @show_on_edit
15
15
  end
16
16
 
17
+ # Validates if the field is visible on certain view
18
+ def visible_on?(view)
19
+ raise "No view specified on visibility check." if view.blank?
20
+
21
+ send :"show_on_#{view.to_s}"
22
+ end
23
+
17
24
  def show_on(*where)
18
25
  return show_on_all if where.include? :all
19
26
 
@@ -44,6 +51,16 @@ module Avo
44
51
  end
45
52
  end
46
53
 
54
+ # When submitting the form on creation, the new page will be create but we don't have a visibility marker for create so we'll default to new
55
+ def show_on_create
56
+ show_on_new
57
+ end
58
+
59
+ # When submitting the form on update, the new page will be create but we don't have a visibility marker for update so we'll default to edit
60
+ def show_on_update
61
+ show_on_edit
62
+ end
63
+
47
64
  private
48
65
 
49
66
  def show_on_view(view)
@@ -29,7 +29,10 @@ module Avo
29
29
  end
30
30
 
31
31
  def frame_url
32
- "#{@resource.record_path}/#{id}?turbo_frame=#{turbo_frame}"
32
+ Avo::Services::URIService.parse(@resource.record_path)
33
+ .append_path(id.to_s)
34
+ .append_query(turbo_frame: turbo_frame.to_s)
35
+ .to_s
33
36
  end
34
37
 
35
38
  # The value
@@ -59,6 +62,22 @@ module Avo
59
62
  def placeholder
60
63
  @placeholder || I18n.t("avo.choose_an_option")
61
64
  end
65
+
66
+ def has_own_panel?
67
+ true
68
+ end
69
+
70
+ def visible_in_reflection?
71
+ false
72
+ end
73
+
74
+ # Adds the view override component
75
+ # has_one, has_many, has_and_belongs_to_many fields don't have edit views
76
+ def component_for_view(view = :index)
77
+ view = :show if view.in? [:new, :create, :update, :edit]
78
+
79
+ super view
80
+ end
62
81
  end
63
82
  end
64
83
  end
@@ -22,7 +22,10 @@ module Avo
22
22
  end
23
23
 
24
24
  def frame_url
25
- "#{@resource.record_path}/#{id}/#{value.id}?turbo_frame=#{turbo_frame}"
25
+ Avo::Services::URIService.parse(@resource.record_path)
26
+ .append_paths(id, value.id)
27
+ .append_query(turbo_frame: turbo_frame)
28
+ .to_s
26
29
  end
27
30
 
28
31
  def fill_field(model, key, value, params)
@@ -13,15 +13,18 @@ module Avo
13
13
  end
14
14
 
15
15
  def cover(field_name, as:, **args, &block)
16
- self.cover_field = self.class.parse_field(field_name, as: as, **args, &block)
16
+ field_parser = Avo::Dsl::FieldParser.new(id: field_name, as: as, order_index: items_index, **args, &block).parse
17
+ self.cover_field = field_parser.instance if field_parser.valid?
17
18
  end
18
19
 
19
20
  def title(field_name, as:, **args, &block)
20
- self.title_field = self.class.parse_field(field_name, as: as, **args, &block)
21
+ field_parser = Avo::Dsl::FieldParser.new(id: field_name, as: as, order_index: items_index, **args, &block).parse
22
+ self.title_field = field_parser.instance if field_parser.valid?
21
23
  end
22
24
 
23
25
  def body(field_name, as:, **args, &block)
24
- self.body_field = self.class.parse_field(field_name, as: as, **args, &block)
26
+ field_parser = Avo::Dsl::FieldParser.new(id: field_name, as: as, order_index: items_index, **args, &block).parse
27
+ self.body_field = field_parser.instance if field_parser.valid?
25
28
  end
26
29
 
27
30
  def hydrate(model:, view:, resource:)