avo 2.9.2.pre1 → 2.10.3.pre.1

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 (141) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +69 -69
  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/country_field/edit_component.html.erb +9 -3
  11. data/app/components/avo/fields/date_field/edit_component.html.erb +26 -7
  12. data/app/components/avo/fields/date_field/index_component.html.erb +7 -1
  13. data/app/components/avo/fields/date_field/show_component.html.erb +7 -1
  14. data/app/components/avo/fields/date_time_field/edit_component.html.erb +1 -0
  15. data/app/components/avo/fields/edit_component.rb +5 -0
  16. data/app/components/avo/fields/file_field/edit_component.html.erb +1 -0
  17. data/app/components/avo/fields/files_field/edit_component.html.erb +1 -0
  18. data/app/components/avo/fields/select_field/edit_component.html.erb +14 -10
  19. data/app/components/avo/fields/show_component.rb +1 -1
  20. data/app/components/avo/index/ordering/button_component.rb +1 -15
  21. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  22. data/app/components/avo/index/resource_controls_component.rb +5 -1
  23. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  24. data/app/components/avo/index/table_row_component.html.erb +1 -1
  25. data/app/components/avo/item_switcher_component.html.erb +19 -0
  26. data/app/components/avo/item_switcher_component.rb +45 -0
  27. data/app/components/avo/panel_component.html.erb +23 -24
  28. data/app/components/avo/panel_component.rb +8 -5
  29. data/app/components/avo/tab_group_component.html.erb +53 -0
  30. data/app/components/avo/tab_group_component.rb +51 -0
  31. data/app/components/avo/tab_switcher_component.html.erb +21 -0
  32. data/app/components/avo/tab_switcher_component.rb +86 -0
  33. data/app/components/avo/views/resource_edit_component.html.erb +34 -56
  34. data/app/components/avo/views/resource_edit_component.rb +11 -1
  35. data/app/components/avo/views/resource_index_component.html.erb +2 -2
  36. data/app/components/avo/views/resource_index_component.rb +3 -3
  37. data/app/components/avo/views/resource_show_component.html.erb +58 -89
  38. data/app/components/avo/views/resource_show_component.rb +2 -2
  39. data/app/controllers/avo/actions_controller.rb +1 -1
  40. data/app/controllers/avo/application_controller.rb +33 -10
  41. data/app/controllers/avo/base_controller.rb +11 -3
  42. data/app/controllers/avo/reorder_controller.rb +25 -0
  43. data/app/helpers/avo/application_helper.rb +6 -6
  44. data/app/helpers/avo/url_helpers.rb +1 -5
  45. data/app/javascript/avo.js +5 -1
  46. data/app/javascript/js/controllers/fields/date_field_controller.js +15 -3
  47. data/app/javascript/js/controllers/loading_button_controller.js +25 -21
  48. data/app/javascript/js/controllers/search_controller.js +3 -0
  49. data/app/javascript/js/controllers/tabs_controller.js +86 -0
  50. data/app/javascript/js/controllers.js +2 -0
  51. data/app/views/avo/base/index.html.erb +1 -1
  52. data/app/views/avo/base/show.html.erb +1 -1
  53. data/app/views/avo/cards/show.html.erb +1 -1
  54. data/app/views/avo/debug/index.html.erb +1 -1
  55. data/app/views/avo/home/_actions.html.erb +1 -1
  56. data/app/views/avo/home/_dashboards.html.erb +19 -0
  57. data/app/views/avo/home/_filters.html.erb +1 -1
  58. data/app/views/avo/home/_resources.html.erb +1 -1
  59. data/app/views/avo/home/failed_to_load.html.erb +1 -1
  60. data/app/views/avo/home/index.html.erb +14 -2
  61. data/app/views/avo/partials/_javascript.html.erb +1 -1
  62. data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
  63. data/app/views/avo/private/design.html.erb +1 -1
  64. data/config/routes.rb +5 -4
  65. data/db/factories.rb +1 -0
  66. data/lib/avo/app.rb +12 -7
  67. data/lib/avo/base_action.rb +2 -19
  68. data/lib/avo/base_card.rb +1 -7
  69. data/lib/avo/base_resource.rb +6 -98
  70. data/lib/avo/base_resource_tool.rb +3 -1
  71. data/lib/avo/concerns/has_fields.rb +249 -50
  72. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  73. data/lib/avo/concerns/is_resource_item.rb +36 -0
  74. data/lib/avo/concerns/model_class_constantized.rb +23 -0
  75. data/lib/avo/configuration.rb +2 -12
  76. data/lib/avo/dashboards/base_dashboard.rb +1 -1
  77. data/lib/avo/dsl/field_parser.rb +83 -0
  78. data/lib/avo/fields/base_field.rb +21 -2
  79. data/lib/avo/fields/country_field.rb +2 -0
  80. data/lib/avo/fields/date_field.rb +13 -9
  81. data/lib/avo/fields/field_extensions/has_include_blank.rb +17 -0
  82. data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
  83. data/lib/avo/fields/file_field.rb +2 -0
  84. data/lib/avo/fields/files_field.rb +2 -0
  85. data/lib/avo/fields/has_base_field.rb +20 -1
  86. data/lib/avo/fields/has_one_field.rb +4 -1
  87. data/lib/avo/fields/key_value_field.rb +4 -4
  88. data/lib/avo/fields/select_field.rb +2 -0
  89. data/lib/avo/grid_collector.rb +6 -3
  90. data/lib/avo/items_holder.rb +68 -0
  91. data/lib/avo/licensing/h_q.rb +12 -0
  92. data/lib/avo/main_panel.rb +3 -0
  93. data/lib/avo/menu/builder.rb +8 -7
  94. data/lib/avo/panel.rb +25 -0
  95. data/lib/avo/panel_builder.rb +23 -0
  96. data/lib/avo/services/uri_service.rb +75 -0
  97. data/lib/avo/tab.rb +78 -0
  98. data/lib/avo/tab_builder.rb +25 -0
  99. data/lib/avo/tab_group.rb +40 -0
  100. data/lib/avo/tab_group_builder.rb +43 -0
  101. data/lib/avo/version.rb +1 -1
  102. data/lib/avo.rb +1 -0
  103. data/lib/generators/avo/action_generator.rb +3 -2
  104. data/lib/generators/avo/base_generator.rb +14 -0
  105. data/lib/generators/avo/card/chartkick_generator.rb +18 -0
  106. data/lib/generators/avo/card/metric_generator.rb +18 -0
  107. data/lib/generators/avo/card/partial_generator.rb +19 -0
  108. data/lib/generators/avo/controller_generator.rb +9 -3
  109. data/lib/generators/avo/dashboard_generator.rb +2 -2
  110. data/lib/generators/avo/eject_generator.rb +2 -3
  111. data/lib/generators/avo/field_generator.rb +2 -2
  112. data/lib/generators/avo/filter_generator.rb +3 -2
  113. data/lib/generators/avo/install_generator.rb +2 -2
  114. data/lib/generators/avo/locales_generator.rb +2 -2
  115. data/lib/generators/avo/named_base_generator.rb +14 -0
  116. data/lib/generators/avo/resource_generator.rb +2 -2
  117. data/lib/generators/avo/resource_tool_generator.rb +4 -4
  118. data/lib/generators/avo/templates/locales/avo.fr.yml +115 -0
  119. data/lib/generators/avo/templates/resource/controller.tt +2 -0
  120. data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
  121. data/lib/generators/avo/templates/tool/view.tt +1 -1
  122. data/lib/generators/avo/tool_generator.rb +4 -4
  123. data/lib/generators/avo/version_generator.rb +23 -0
  124. data/public/avo-assets/avo.css +31 -3
  125. data/public/avo-assets/avo.js +72 -72
  126. data/public/avo-assets/avo.js.map +3 -3
  127. metadata +32 -16
  128. data/app/assets/builds/action_cable.js +0 -2
  129. data/app/assets/builds/action_cable.js.map +0 -7
  130. data/app/assets/builds/application.js +0 -2
  131. data/app/assets/builds/application.js.map +0 -7
  132. data/app/assets/builds/avo.css +0 -9028
  133. data/app/assets/builds/avo.js +0 -512
  134. data/app/assets/builds/avo.js.map +0 -7
  135. data/app/assets/builds/avo_custom.js +0 -6
  136. data/app/assets/builds/avo_custom.js.map +0 -7
  137. data/lib/avo/concerns/has_tools.rb +0 -47
  138. data/lib/avo/fields/currency_field.rb +0 -15
  139. data/lib/generators/avo/chartkick_card_generator.rb +0 -16
  140. data/lib/generators/avo/metric_card_generator.rb +0 -16
  141. data/lib/generators/avo/partial_card_generator.rb +0 -17
@@ -4,88 +4,287 @@ 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.respond_to?(:foreign_key) &&
187
+ reflection.inverse_of.foreign_key == field.foreign_key
188
+ is_valid = false
189
+ end
76
190
 
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)
191
+ # polymorphic association
192
+ if field.respond_to?(:foreign_key) &&
193
+ field.is_polymorphic? &&
194
+ reflection.respond_to?(:polymorphic?) &&
195
+ reflection.inverse_of.respond_to?(:foreign_key) &&
196
+ reflection.inverse_of.foreign_key == field.reflection.foreign_key
197
+ is_valid = false
198
+ end
199
+ end
80
200
  end
201
+
202
+ is_valid
203
+ end
204
+
205
+ if panel.present?
206
+ fields = fields.select do |field|
207
+ field.panel_name == panel
81
208
  end
82
209
  end
83
210
 
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)
211
+ hydrate_fields(model: @model, view: @view)
212
+
213
+ fields
214
+ end
215
+
216
+ def get_field(id)
217
+ get_field_definitions.find do |f|
218
+ f.id == id.to_sym
219
+ end
220
+ end
221
+
222
+ def tools
223
+ check_license
224
+
225
+ return [] if App.license.lacks_with_trial :resource_tools
226
+ return [] if self.class.tools.blank?
227
+
228
+ self.items
229
+ .select do |item|
230
+ next if item.nil?
231
+
232
+ item.is_tool?
88
233
  end
234
+ .map do |tool|
235
+ tool.hydrate view: view
236
+ tool
237
+ end
238
+ .select do |item|
239
+ item.visible_on?(view)
240
+ end
241
+ end
242
+
243
+ # Separates the fields that are in a panel and those that are just hanging out.
244
+ # Take the ones that aren't placed into a panel and add them to the "default" panel.
245
+ # This is to keep compatibility with the versions before 2.10 when you didn't have the ability to add fields to panels.
246
+ def get_items
247
+ panelless_items = []
248
+ panelfull_items = []
249
+
250
+ items.each do |item|
251
+ # fields and tabs can be hidden on some views
252
+ if item.respond_to? :visible_on?
253
+ next unless item.visible_on?(view)
254
+ end
255
+ if item.respond_to? :visible?
256
+ next unless item.visible?
257
+ end
258
+
259
+ if item.is_field?
260
+ if item.has_own_panel?
261
+ panelfull_items << item
262
+ else
263
+ panelless_items << item
264
+ end
265
+ else
266
+ panelfull_items << item
267
+ end
268
+ end
269
+
270
+ # Add all the panelles fields to a new panel
271
+ main_panel_holder = Avo::ItemsHolder.new
272
+ main_panel_holder.items = panelless_items
273
+
274
+ # Add that panel to the main panel
275
+ main_panel = Avo::MainPanel.new
276
+ main_panel.items_holder = main_panel_holder
277
+
278
+ # Return all the items but this time with all the panelless ones inside the main panel
279
+ [main_panel, *panelfull_items]
280
+ end
281
+
282
+ private
283
+
284
+ def check_license
285
+ if !Rails.env.production? && App.license.present? && App.license.lacks(:resource_tools)
286
+ # Add error message to let the developer know the resource tool will not be available in a production environment.
287
+ 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
288
  end
90
289
  end
91
290
  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
@@ -40,7 +40,7 @@ module Avo
40
40
  @per_page = 24
41
41
  @per_page_steps = [12, 24, 48, 72]
42
42
  @via_per_page = 8
43
- @locale = "en-US"
43
+ @locale = nil
44
44
  @currency = "USD"
45
45
  @default_view_type = :table
46
46
  @license = "community"
@@ -78,16 +78,6 @@ module Avo
78
78
  @profile_menu = nil
79
79
  end
80
80
 
81
- def locale_tag
82
- ::ISO::Tag.new(locale)
83
- end
84
-
85
- def language_code
86
- locale_tag.language.code
87
- rescue
88
- "en"
89
- end
90
-
91
81
  def current_user_method(&block)
92
82
  @current_user = block if block.present?
93
83
  end
@@ -123,7 +113,7 @@ module Avo
123
113
  end
124
114
 
125
115
  def computed_root_path
126
- Avo::App.root_path
116
+ Avo.configuration.root_path
127
117
  end
128
118
 
129
119
  def feature_enabled?(feature)
@@ -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
@@ -72,6 +76,8 @@ module Avo
72
76
  @index_text_align = args[:index_text_align] || :left
73
77
  @html = args[:html] || nil
74
78
 
79
+ @args = args
80
+
75
81
  @updatable = true
76
82
  @computable = true
77
83
  @computed = block.present?
@@ -206,9 +212,14 @@ module Avo
206
212
 
207
213
  # Try and build the component class or fallback to a blank one
208
214
  def component_for_view(view = :index)
215
+ # Use the edit variant for all "update" views
216
+ view = :edit if view.in? [:new, :create, :update]
217
+
209
218
  component_class = "::Avo::Fields::#{view_component_name}::#{view.to_s.camelize}Component"
210
219
  component_class.constantize
211
220
  rescue
221
+ # When returning nil, a race condition happens and throws an error in some environments.
222
+ # See https://github.com/avo-hq/avo/pull/365
212
223
  ::Avo::BlankFieldComponent
213
224
  end
214
225
 
@@ -228,6 +239,14 @@ module Avo
228
239
  true
229
240
  end
230
241
 
242
+ def visible_in_reflection?
243
+ true
244
+ end
245
+
246
+ def hidden_in_reflection?
247
+ !visible_in_reflection?
248
+ end
249
+
231
250
  private
232
251
 
233
252
  def model_or_class(model)
@@ -1,6 +1,8 @@
1
1
  module Avo
2
2
  module Fields
3
3
  class CountryField < BaseField
4
+ include Avo::Fields::FieldExtensions::HasIncludeBlank
5
+
4
6
  attr_reader :countries
5
7
  attr_reader :display_code
6
8
 
@@ -3,26 +3,30 @@ module Avo
3
3
  class DateField < TextField
4
4
  attr_reader :first_day_of_week
5
5
  attr_reader :picker_format
6
+ attr_reader :disable_mobile
6
7
  attr_reader :format
7
8
  attr_reader :relative
8
9
 
9
10
  def initialize(id, **args, &block)
10
11
  super(id, **args, &block)
11
12
 
12
- @first_day_of_week = args[:first_day_of_week].present? ? args[:first_day_of_week].to_i : 0
13
- @picker_format = args[:picker_format].present? ? args[:picker_format] : "Y-m-d"
14
- @format = args[:format].present? ? args[:format] : :long
15
- @relative = args[:relative].present? ? args[:relative] : false
13
+ add_string_prop args, :first_day_of_week, 0
14
+ add_string_prop args, :picker_format, "Y-m-d"
15
+ add_string_prop args, :format, "yyyy-LL-dd"
16
+ add_boolean_prop args, :relative
17
+ add_boolean_prop args, :disable_mobile
16
18
  end
17
19
 
18
20
  def formatted_value
19
21
  return if value.blank?
20
22
 
21
- if @format.is_a?(Symbol)
22
- value.to_formatted_s(@format)
23
- else
24
- value.strftime(@format)
25
- end
23
+ value.iso8601
24
+ end
25
+
26
+ def edit_formatted_value
27
+ return nil if value.nil?
28
+
29
+ value.iso8601
26
30
  end
27
31
  end
28
32
  end
@@ -0,0 +1,17 @@
1
+ module Avo
2
+ module Fields
3
+ module FieldExtensions
4
+ module HasIncludeBlank
5
+ def include_blank
6
+ if @args[:include_blank] == true
7
+ placeholder || '—'
8
+ elsif @args[:include_blank] == false
9
+ false
10
+ else
11
+ @args[:include_blank]
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end