avo 2.9.2.pre1 → 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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -7
  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/date_field/edit_component.html.erb +1 -0
  11. data/app/components/avo/fields/date_time_field/edit_component.html.erb +10 -25
  12. data/app/components/avo/fields/date_time_field/index_component.html.erb +1 -9
  13. data/app/components/avo/fields/date_time_field/show_component.html.erb +1 -9
  14. data/app/components/avo/fields/edit_component.rb +5 -0
  15. data/app/components/avo/fields/show_component.rb +1 -1
  16. data/app/components/avo/index/ordering/button_component.rb +2 -12
  17. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  18. data/app/components/avo/index/resource_controls_component.rb +5 -1
  19. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  20. data/app/components/avo/index/table_row_component.html.erb +1 -1
  21. data/app/components/avo/item_switcher_component.html.erb +19 -0
  22. data/app/components/avo/item_switcher_component.rb +45 -0
  23. data/app/components/avo/panel_component.html.erb +23 -24
  24. data/app/components/avo/panel_component.rb +8 -5
  25. data/app/components/avo/tab_group_component.html.erb +53 -0
  26. data/app/components/avo/tab_group_component.rb +51 -0
  27. data/app/components/avo/tab_switcher_component.html.erb +21 -0
  28. data/app/components/avo/tab_switcher_component.rb +86 -0
  29. data/app/components/avo/views/resource_edit_component.html.erb +34 -56
  30. data/app/components/avo/views/resource_edit_component.rb +11 -1
  31. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  32. data/app/components/avo/views/resource_index_component.rb +3 -3
  33. data/app/components/avo/views/resource_show_component.html.erb +58 -89
  34. data/app/components/avo/views/resource_show_component.rb +2 -2
  35. data/app/controllers/avo/actions_controller.rb +1 -1
  36. data/app/controllers/avo/application_controller.rb +20 -3
  37. data/app/helpers/avo/application_helper.rb +0 -6
  38. data/app/helpers/avo/url_helpers.rb +1 -1
  39. data/app/javascript/avo.js +5 -1
  40. data/app/javascript/js/controllers/fields/date_field_controller.js +25 -87
  41. data/app/javascript/js/controllers/loading_button_controller.js +25 -21
  42. data/app/javascript/js/controllers/tabs_controller.js +86 -0
  43. data/app/javascript/js/controllers.js +2 -0
  44. data/app/views/avo/base/index.html.erb +1 -1
  45. data/app/views/avo/base/show.html.erb +1 -1
  46. data/app/views/avo/cards/show.html.erb +1 -1
  47. data/app/views/avo/debug/index.html.erb +1 -1
  48. data/app/views/avo/home/_actions.html.erb +1 -1
  49. data/app/views/avo/home/_dashboards.html.erb +19 -0
  50. data/app/views/avo/home/_filters.html.erb +1 -1
  51. data/app/views/avo/home/_resources.html.erb +1 -1
  52. data/app/views/avo/home/failed_to_load.html.erb +1 -1
  53. data/app/views/avo/home/index.html.erb +14 -2
  54. data/app/views/avo/partials/_javascript.html.erb +1 -1
  55. data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
  56. data/app/views/avo/private/design.html.erb +1 -1
  57. data/config/routes.rb +1 -1
  58. data/db/migrate/20210421064037_add_color_to_teams.rb +5 -0
  59. data/db/migrate/20210423075924_add_progress_to_projects.rb +5 -0
  60. data/db/migrate/20210525143134_add_slug_to_users.rb +6 -0
  61. data/lib/avo/app.rb +11 -4
  62. data/lib/avo/base_action.rb +2 -19
  63. data/lib/avo/base_card.rb +1 -7
  64. data/lib/avo/base_resource.rb +1 -95
  65. data/lib/avo/base_resource_tool.rb +3 -1
  66. data/lib/avo/concerns/handles_field_args.rb +1 -1
  67. data/lib/avo/concerns/has_fields.rb +247 -50
  68. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  69. data/lib/avo/concerns/is_resource_item.rb +36 -0
  70. data/lib/avo/concerns/model_class_constantized.rb +23 -0
  71. data/lib/avo/dashboards/base_dashboard.rb +1 -1
  72. data/lib/avo/dsl/field_parser.rb +83 -0
  73. data/lib/avo/fields/base_field.rb +19 -2
  74. data/lib/avo/fields/date_field.rb +2 -0
  75. data/lib/avo/fields/date_time_field.rb +9 -21
  76. data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
  77. data/lib/avo/fields/has_base_field.rb +20 -1
  78. data/lib/avo/fields/has_one_field.rb +4 -1
  79. data/lib/avo/grid_collector.rb +6 -3
  80. data/lib/avo/items_holder.rb +68 -0
  81. data/lib/avo/licensing/h_q.rb +10 -0
  82. data/lib/avo/main_panel.rb +3 -0
  83. data/lib/avo/menu/builder.rb +8 -7
  84. data/lib/avo/panel.rb +25 -0
  85. data/lib/avo/panel_builder.rb +23 -0
  86. data/lib/avo/services/uri_service.rb +71 -0
  87. data/lib/avo/tab.rb +78 -0
  88. data/lib/avo/tab_builder.rb +25 -0
  89. data/lib/avo/tab_group.rb +40 -0
  90. data/lib/avo/tab_group_builder.rb +43 -0
  91. data/lib/avo/version.rb +1 -1
  92. data/lib/avo.rb +1 -0
  93. data/lib/generators/avo/templates/locales/avo.fr.yml +115 -0
  94. data/lib/generators/avo/templates/resource/controller.tt +2 -0
  95. data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
  96. data/lib/generators/avo/templates/tool/view.tt +1 -1
  97. data/public/avo-assets/avo.css +27 -3
  98. data/public/avo-assets/avo.js +77 -77
  99. data/public/avo-assets/avo.js.map +3 -3
  100. metadata +28 -13
  101. data/app/assets/builds/action_cable.js +0 -2
  102. data/app/assets/builds/action_cable.js.map +0 -7
  103. data/app/assets/builds/application.js +0 -2
  104. data/app/assets/builds/application.js.map +0 -7
  105. data/app/assets/builds/avo.css +0 -9028
  106. data/app/assets/builds/avo.js +0 -512
  107. data/app/assets/builds/avo.js.map +0 -7
  108. data/app/assets/builds/avo_custom.js +0 -6
  109. data/app/assets/builds/avo_custom.js.map +0 -7
  110. data/lib/avo/concerns/has_tools.rb +0 -47
@@ -4,10 +4,10 @@ module Avo
4
4
  extend HasContext
5
5
 
6
6
  include ActionView::Helpers::UrlHelper
7
- include Avo::Concerns::HasTools
8
7
  include Avo::Concerns::HasModel
9
8
  include Avo::Concerns::HasFields
10
9
  include Avo::Concerns::HasStimulusControllers
10
+ include Avo::Concerns::ModelClassConstantized
11
11
 
12
12
  delegate :view_context, to: ::Avo::App
13
13
  delegate :simple_format, :content_tag, to: :view_context
@@ -29,7 +29,6 @@ module Avo
29
29
  class_attribute :search_query, default: nil
30
30
  class_attribute :search_query_help, default: ""
31
31
  class_attribute :includes, default: []
32
- class_attribute :model_class
33
32
  class_attribute :translation_key
34
33
  class_attribute :default_view_type, default: :table
35
34
  class_attribute :devise_password_optional, default: false
@@ -44,7 +43,6 @@ module Avo
44
43
  class_attribute :hide_from_global_search, default: false
45
44
  class_attribute :after_create_path, default: :show
46
45
  class_attribute :after_update_path, default: :show
47
- class_attribute :invalid_fields
48
46
  class_attribute :record_selector, default: true
49
47
  class_attribute :keep_filters_panel_open, default: false
50
48
 
@@ -123,80 +121,6 @@ module Avo
123
121
  self
124
122
  end
125
123
 
126
- def get_field_definitions
127
- return [] if self.class.fields.blank?
128
-
129
- fields = self.class.fields.map do |field|
130
- field.hydrate(resource: self, panel_name: default_panel_name, user: user)
131
- end
132
-
133
- if Avo::App.license.lacks_with_trial(:custom_fields)
134
- fields = fields.reject do |field|
135
- field.custom?
136
- end
137
- end
138
-
139
- if Avo::App.license.lacks_with_trial(:advanced_fields)
140
- fields = fields.reject do |field|
141
- field.type == "tags"
142
- end
143
- end
144
-
145
- fields
146
- end
147
-
148
- def get_fields(panel: nil, reflection: nil)
149
- fields = get_field_definitions
150
- .select do |field|
151
- field.send("show_on_#{@view}")
152
- end
153
- .select do |field|
154
- field.visible?
155
- end
156
- .select do |field|
157
- is_valid = true
158
-
159
- # Strip out the reflection field in index queries with a parent association.
160
- if reflection.present?
161
- # regular non-polymorphic association
162
- # we're matching the reflection inverse_of foriegn key with the field's foreign_key
163
- if field.is_a?(Avo::Fields::BelongsToField)
164
- if field.respond_to?(:foreign_key) &&
165
- reflection.inverse_of.present? &&
166
- reflection.inverse_of.foreign_key == field.foreign_key
167
- is_valid = false
168
- end
169
-
170
- # polymorphic association
171
- if field.respond_to?(:foreign_key) &&
172
- field.is_polymorphic? &&
173
- reflection.respond_to?(:polymorphic?) &&
174
- reflection.inverse_of.foreign_key == field.reflection.foreign_key
175
- is_valid = false
176
- end
177
- end
178
- end
179
-
180
- is_valid
181
- end
182
-
183
- if panel.present?
184
- fields = fields.select do |field|
185
- field.panel_name == panel
186
- end
187
- end
188
-
189
- hydrate_fields(model: @model, view: @view)
190
-
191
- fields
192
- end
193
-
194
- def get_field(id)
195
- get_field_definitions.find do |f|
196
- f.id == id.to_sym
197
- end
198
- end
199
-
200
124
  def get_grid_fields
201
125
  return if self.class.grid_loader.blank?
202
126
 
@@ -215,14 +139,6 @@ module Avo
215
139
  self.class.actions_loader.bag
216
140
  end
217
141
 
218
- def hydrate_fields(model: nil, view: nil)
219
- fields.map do |field|
220
- field.hydrate(model: @model, view: @view, resource: self)
221
- end
222
-
223
- self
224
- end
225
-
226
142
  def default_panel_name
227
143
  return @params[:related_name].capitalize if @params.present? && @params[:related_name].present?
228
144
 
@@ -236,16 +152,6 @@ module Avo
236
152
  end
237
153
  end
238
154
 
239
- def panels
240
- [
241
- {
242
- name: default_panel_name,
243
- type: :fields,
244
- in_panel: true
245
- }
246
- ]
247
- end
248
-
249
155
  def class_name_without_resource
250
156
  self.class.name.demodulize.chomp("Resource")
251
157
  end
@@ -1,9 +1,11 @@
1
1
  module Avo
2
2
  class BaseResourceTool
3
+ include Avo::Concerns::IsResourceItem
3
4
  include Avo::Fields::FieldExtensions::VisibleInDifferentViews
4
5
 
5
6
  class_attribute :name
6
7
  class_attribute :partial
8
+ class_attribute :item_type, default: :tool
7
9
 
8
10
  attr_accessor :params
9
11
  attr_accessor :resource
@@ -20,7 +22,7 @@ module Avo
20
22
  end
21
23
 
22
24
  def hydrate(view: nil)
23
- @view = view
25
+ @view = view if view.present?
24
26
 
25
27
  self
26
28
  end
@@ -28,7 +28,7 @@ module Avo
28
28
  add_prop_from_args args, name: name, default: default, type: :array
29
29
  end
30
30
 
31
- def add_string_prop(args, name, default = nil)
31
+ def add_string_prop(args, name, default = [])
32
32
  add_prop_from_args args, name: name, default: default, type: :string
33
33
  end
34
34
  end
@@ -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
@@ -0,0 +1,23 @@
1
+ module Avo
2
+ module Concerns
3
+ module ModelClassConstantized
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ attr_reader :model_class
8
+
9
+ # Cast the model class to a constantized version and memoize it like that
10
+ def model_class=(value)
11
+ @model_class = case value
12
+ when Class
13
+ value
14
+ when String, Symbol
15
+ value.to_s.safe_constantize
16
+ else
17
+ raise ArgumentError.new "Failed to find a proper model class for #{self.to_s}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ 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