avo 3.0.1.beta7 → 3.0.1.beta9

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 +2 -2
  3. data/Gemfile.lock +23 -25
  4. data/{public/avo-assets/avo.css → app/assets/builds/avo.base.css} +1943 -1552
  5. data/app/assets/builds/avo.base.js +124556 -0
  6. data/app/assets/builds/avo.base.js.map +7 -0
  7. data/app/assets/builds/avo.custom.js +6 -0
  8. data/app/assets/builds/avo.custom.js.map +7 -0
  9. data/app/assets/stylesheets/avo.base.css +1 -0
  10. data/app/assets/stylesheets/css/fields/tags.css +32 -0
  11. data/app/components/avo/actions_component.rb +1 -1
  12. data/app/components/avo/field_wrapper_component.html.erb +1 -1
  13. data/app/components/avo/field_wrapper_component.rb +1 -7
  14. data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
  15. data/app/components/avo/fields/common/badge_viewer_component.html.erb +1 -24
  16. data/app/components/avo/fields/common/badge_viewer_component.rb +24 -0
  17. data/app/components/avo/fields/common/boolean_check_component.html.erb +1 -12
  18. data/app/components/avo/fields/common/boolean_check_component.rb +2 -1
  19. data/app/components/avo/fields/common/gravatar_viewer_component.html.erb +1 -1
  20. data/app/components/avo/fields/common/gravatar_viewer_component.rb +2 -2
  21. data/app/components/avo/fields/common/heading_component.html.erb +3 -8
  22. data/app/components/avo/fields/common/heading_component.rb +1 -3
  23. data/app/components/avo/fields/common/key_value_component.html.erb +2 -2
  24. data/app/components/avo/fields/common/key_value_component.rb +2 -2
  25. data/app/components/avo/fields/common/progress_bar_component.rb +3 -9
  26. data/app/components/avo/fields/edit_component.rb +1 -1
  27. data/app/components/avo/fields/external_image_field/index_component.html.erb +1 -1
  28. data/app/components/avo/fields/file_field/index_component.html.erb +2 -2
  29. data/app/components/avo/fields/gravatar_field/index_component.html.erb +1 -1
  30. data/app/components/avo/fields/heading_field/edit_component.html.erb +1 -1
  31. data/app/components/avo/fields/heading_field/show_component.html.erb +1 -1
  32. data/app/components/avo/fields/id_field/index_component.html.erb +1 -1
  33. data/app/components/avo/fields/index_component.rb +1 -1
  34. data/app/components/avo/fields/show_component.rb +1 -1
  35. data/app/components/avo/fields/text_field/index_component.html.erb +1 -1
  36. data/app/components/avo/index/field_wrapper_component.rb +1 -1
  37. data/app/components/avo/index/resource_controls_component.rb +1 -1
  38. data/app/components/avo/index/resource_table_component.rb +3 -9
  39. data/app/components/avo/resource_component.rb +9 -4
  40. data/app/components/avo/sidebar_profile_component.html.erb +4 -4
  41. data/app/components/avo/tab_group_component.html.erb +1 -1
  42. data/app/components/avo/tab_switcher_component.rb +2 -2
  43. data/app/components/avo/views/resource_edit_component.html.erb +9 -20
  44. data/app/components/avo/views/resource_edit_component.rb +5 -5
  45. data/app/components/avo/views/resource_index_component.rb +1 -1
  46. data/app/components/avo/views/resource_show_component.rb +1 -1
  47. data/app/controllers/avo/actions_controller.rb +9 -20
  48. data/app/controllers/avo/application_controller.rb +5 -5
  49. data/app/controllers/avo/base_controller.rb +1 -1
  50. data/app/controllers/avo/search_controller.rb +19 -2
  51. data/app/controllers/concerns/avo/initializes_avo.rb +1 -0
  52. data/app/helpers/avo/url_helpers.rb +1 -1
  53. data/app/views/avo/actions/show.html.erb +1 -1
  54. data/config/routes.rb +4 -3
  55. data/db/factories.rb +1 -1
  56. data/lib/avo/base_action.rb +20 -25
  57. data/lib/avo/base_resource.rb +7 -6
  58. data/lib/avo/concerns/filters_session_handler.rb +3 -3
  59. data/lib/avo/concerns/has_helpers.rb +18 -0
  60. data/lib/avo/concerns/has_items.rb +1 -5
  61. data/lib/avo/concerns/visible_in_different_views.rb +7 -1
  62. data/lib/avo/concerns/visible_items.rb +43 -0
  63. data/lib/avo/configuration.rb +8 -2
  64. data/lib/avo/current.rb +1 -0
  65. data/lib/avo/execution_context.rb +4 -1
  66. data/lib/avo/fields/badge_field.rb +1 -1
  67. data/lib/avo/fields/base_field.rb +21 -44
  68. data/lib/avo/fields/belongs_to_field.rb +1 -1
  69. data/lib/avo/fields/concerns/has_html_attributes.rb +2 -0
  70. data/lib/avo/fields/concerns/use_view_components.rb +45 -0
  71. data/lib/avo/fields/external_image_field.rb +2 -2
  72. data/lib/avo/fields/file_field.rb +2 -2
  73. data/lib/avo/fields/gravatar_field.rb +2 -2
  74. data/lib/avo/fields/has_base_field.rb +2 -2
  75. data/lib/avo/fields/heading_field.rb +5 -13
  76. data/lib/avo/fields/id_field.rb +2 -2
  77. data/lib/avo/fields/text_field.rb +2 -2
  78. data/lib/avo/resources/items/holder.rb +0 -6
  79. data/lib/avo/resources/items/item_group.rb +2 -2
  80. data/lib/avo/resources/items/row.rb +3 -3
  81. data/lib/avo/resources/items/sidebar.rb +1 -1
  82. data/lib/avo/resources/items/tab.rb +2 -2
  83. data/lib/avo/resources/items/tab_group.rb +1 -1
  84. data/lib/avo/resources/resource_manager.rb +6 -1
  85. data/lib/avo/version.rb +1 -1
  86. data/lib/avo/view_inquirer.rb +36 -0
  87. data/lib/avo.rb +9 -0
  88. data/lib/generators/avo/eject_generator.rb +93 -16
  89. data/lib/generators/avo/js/install_generator.rb +2 -2
  90. data/lib/generators/avo/resource_generator.rb +5 -5
  91. data/lib/generators/avo/tailwindcss/install_generator.rb +1 -1
  92. data/lib/generators/avo/templates/initializer/avo.tt +5 -0
  93. data/public/avo-assets/avo.base.css +54 -219
  94. metadata +12 -6
  95. data/config/master.key +0 -1
  96. data/public/avo-assets/avo.js +0 -513
  97. data/public/avo-assets/avo.js.map +0 -7
data/config/routes.rb CHANGED
@@ -4,9 +4,10 @@ Avo::Engine.routes.draw do
4
4
  get "resources", to: redirect(Avo.configuration.root_path)
5
5
  get "dashboards", to: redirect(Avo.configuration.root_path)
6
6
 
7
- mount Avo::DynamicFilters::Engine, at: "/avo-dynamic_filters" if defined?(Avo::DynamicFilters::Engine)
8
- mount Avo::Dashboards::Engine, at: "/dashboards" if defined?(Avo::Dashboards::Engine)
9
- mount Avo::Pro::Engine, at: "/avo-pro" if defined?(Avo::Pro::Engine)
7
+ # Mount Avo engines routes by default but leave it configurable in case the user wants to nest these under a scope.
8
+ if Avo.configuration.mount_avo_engines
9
+ instance_exec(&Avo.mount_engines)
10
+ end
10
11
 
11
12
  post "/rails/active_storage/direct_uploads", to: "/active_storage/direct_uploads#create"
12
13
 
data/db/factories.rb CHANGED
@@ -38,7 +38,7 @@ FactoryBot.define do
38
38
  factory :project do
39
39
  name { Faker::App.name }
40
40
  status { ['closed', :rejected, :failed, 'loading', :running, :waiting].sample }
41
- stage { ["Discovery", "Idea", "Done", "On hold", "Cancelled"].sample }
41
+ stage { ["Discovery", "Idea", "Done", "On hold", "Cancelled", "Drafting"].sample }
42
42
  budget { Faker::Number.decimal(l_digits: 4) }
43
43
  country { Faker::Address.country_code }
44
44
  description { Faker::Markdown.sandwich(sentences: 5) }
@@ -65,7 +65,7 @@ module Avo
65
65
  self.class.record = record
66
66
  self.class.resource = resource
67
67
  self.class.user = user
68
- self.class.view = view
68
+ self.class.view = Avo::ViewInquirer.new(view)
69
69
  @arguments = arguments
70
70
 
71
71
  self.class.message ||= I18n.t("avo.are_you_sure_you_want_to_run_this_option")
@@ -101,39 +101,34 @@ module Avo
101
101
  end
102
102
 
103
103
  def handle_action(**args)
104
- records, fields, current_user, resource = args.values_at(:records, :fields, :current_user, :resource)
105
- # Fetching the field definitions and not the actual fields (get_fields) because they will break if the user uses a `visible` block and adds a condition using the `params` variable. The params are different in the show method and the handle method.
106
- action_fields = get_field_definitions.map { |field| [field.id, field] }.to_h
107
-
108
- # For some fields, like belongs_to, the id and database_id differ (user vs user_id).
109
- # That's why we need to fetch the database_id for when we process the action.
110
- action_fields_by_database_id = action_fields.map do |id, value|
111
- [value.database_id.to_sym, value]
112
- end.to_h
104
+ processed_fields = if args[:fields].present?
105
+ # Fetching the field definitions and not the actual fields (get_fields) because they will break if the user uses a `visible` block and adds a condition using the `params` variable. The params are different in the show method and the handle method.
106
+ action_fields = get_field_definitions.map { |field| [field.id, field] }.to_h
107
+
108
+ # For some fields, like belongs_to, the id and database_id differ (user vs user_id).
109
+ # That's why we need to fetch the database_id for when we process the action.
110
+ action_fields_by_database_id = action_fields.map do |id, value|
111
+ [value.database_id.to_sym, value]
112
+ end.to_h
113
113
 
114
- if fields.present?
115
- processed_fields = fields.to_unsafe_h.map do |name, value|
114
+ args[:fields].to_unsafe_h.map do |name, value|
116
115
  field = action_fields_by_database_id[name.to_sym]
117
116
 
118
117
  next if field.blank?
119
118
 
120
119
  [name, field.resolve_attribute(value)]
121
- end
122
-
123
- processed_fields = processed_fields.reject(&:blank?).to_h
120
+ end.reject(&:blank?).to_h
124
121
  else
125
- processed_fields = {}
122
+ {}
126
123
  end
127
124
 
128
- args = {
125
+ handle(
129
126
  fields: processed_fields.with_indifferent_access,
130
- current_user: current_user,
131
- resource: resource
132
- }
133
-
134
- args[:records] = records
135
-
136
- handle(**args)
127
+ current_user: args[:current_user],
128
+ resource: args[:resource],
129
+ records: args[:query],
130
+ query: args[:query]
131
+ )
137
132
 
138
133
  self
139
134
  end
@@ -141,7 +136,7 @@ module Avo
141
136
  def visible_in_view(parent_resource: nil)
142
137
  if visible.blank?
143
138
  # Hide on the :new view by default
144
- return false if view == :new
139
+ return false if view.new?
145
140
 
146
141
  # Show on all other views
147
142
  return true
@@ -9,6 +9,7 @@ module Avo
9
9
  include Avo::Concerns::HasStimulusControllers
10
10
  include Avo::Concerns::ModelClassConstantized
11
11
  include Avo::Concerns::HasDescription
12
+ include Avo::Concerns::HasHelpers
12
13
 
13
14
  # Avo::Current methods
14
15
  delegate :context, to: Avo::Current
@@ -233,14 +234,14 @@ module Avo
233
234
  delegate :model_key, to: :class
234
235
 
235
236
  def initialize(record: nil, view: nil, user: nil, params: nil)
236
- @view = view if view.present?
237
+ @view = Avo::ViewInquirer.new(view) if view.present?
237
238
  @user = user if user.present?
238
239
  @params = params if params.present?
239
240
 
240
241
  if record.present?
241
242
  @record = record
242
243
 
243
- hydrate_model_with_default_values if @view == :new
244
+ hydrate_model_with_default_values if @view&.new?
244
245
  end
245
246
 
246
247
  detect_fields
@@ -295,7 +296,7 @@ module Avo
295
296
  # def get_action_arguments / def get_filter_arguments / def get_scope_arguments
296
297
  define_method "get_#{entity}_arguments" do |entity_class|
297
298
  klass = send("get_#{plural_entity}").find { |entity| entity[:class].to_s == entity_class.to_s }
298
-
299
+
299
300
  raise "Couldn't find '#{entity_class}' in the 'def #{plural_entity}' method on your '#{self.class}' resource." if klass.nil?
300
301
 
301
302
  klass[:arguments]
@@ -303,14 +304,14 @@ module Avo
303
304
  end
304
305
 
305
306
  def hydrate(record: nil, view: nil, user: nil, params: nil)
306
- @view = view if view.present?
307
+ @view = Avo::ViewInquirer.new(view) if view.present?
307
308
  @user = user if user.present?
308
309
  @params = params if params.present?
309
310
 
310
311
  if record.present?
311
312
  @record = record
312
313
 
313
- hydrate_model_with_default_values if @view == :new
314
+ hydrate_model_with_default_values if @view&.new?
314
315
  end
315
316
 
316
317
  self
@@ -445,7 +446,7 @@ module Avo
445
446
  def hydrate_model_with_default_values
446
447
  default_values = get_fields
447
448
  .select do |field|
448
- !field.computed
449
+ !field.computed && !field.is_a?(Avo::Fields::HeadingField)
449
450
  end
450
451
  .map do |field|
451
452
  value = field.value
@@ -26,16 +26,16 @@ module Avo
26
26
  end
27
27
 
28
28
  def filters_session_key
29
- @filters_session_key ||= '/filters/' << %w[
29
+ @filters_session_key ||= "/filters/" << %w[
30
30
  turbo_frame controller resource_name related_name
31
31
  action id
32
- ].map { |key| params[key] }.compact.join('/')
32
+ ].map { |key| params[key] }.compact.join("/")
33
33
  end
34
34
 
35
35
  def cache_resource_filters?
36
36
  Avo::ExecutionContext.new(
37
37
  target: Avo.configuration.cache_resource_filters,
38
- current_user: current_user,
38
+ current_user: _current_user,
39
39
  resource: @resource
40
40
  ).handle
41
41
  end
@@ -0,0 +1,18 @@
1
+ module Avo
2
+ module Concerns
3
+ module HasHelpers
4
+ def helpers
5
+ @helpers ||= Class.new do
6
+ def initialize
7
+ helper_names = ActionController::Base.all_helpers_from_path Rails.root.join("app", "helpers")
8
+ helpers = ActionController::Base.modules_for_helpers helper_names
9
+
10
+ helpers.each do |helper|
11
+ extend helper
12
+ end
13
+ end
14
+ end.new
15
+ end
16
+ end
17
+ end
18
+ end
@@ -30,10 +30,6 @@ module Avo
30
30
  deprecated_dsl_api __method__, "fields"
31
31
  end
32
32
 
33
- def heading(body, **args)
34
- deprecated_dsl_api __method__, "fields"
35
- end
36
-
37
33
  def sidebar(**args, &block)
38
34
  deprecated_dsl_api __method__, "fields"
39
35
  end
@@ -273,7 +269,7 @@ module Avo
273
269
  end
274
270
  .select do |item|
275
271
  # On location field we can have field coordinates and setters with different names like latitude and longitude
276
- if !item.is_a?(Avo::Fields::LocationField) && !item.is_heading? && view.in?([:edit, :update, :new, :create])
272
+ if !item.is_a?(Avo::Fields::LocationField) && !item.is_heading? && view.in?(%w[edit update new create])
277
273
  if item.respond_to?(:id)
278
274
  item.resource.record.respond_to?("#{item.id}=")
279
275
  else
@@ -61,7 +61,7 @@ module Avo
61
61
  def except_on(*where)
62
62
  show_on_all
63
63
  normalize_views(where).flatten.each do |view|
64
- show_on_view view
64
+ hide_on_view view
65
65
  end
66
66
  end
67
67
 
@@ -113,12 +113,18 @@ module Avo
113
113
 
114
114
  def normalize_views(*views_and_groups)
115
115
  forms = views_and_groups.flatten! & [:forms]
116
+ display = views_and_groups & [:display]
116
117
 
117
118
  if forms.present?
118
119
  views_and_groups -= forms
119
120
  views_and_groups += [:new, :edit]
120
121
  end
121
122
 
123
+ if display.present?
124
+ views_and_groups -= display
125
+ views_and_groups += [:index, :show]
126
+ end
127
+
122
128
  views_and_groups.flatten.uniq
123
129
  end
124
130
  end
@@ -0,0 +1,43 @@
1
+ # This concern helps us figure out what items are visible for each tab, panel or sidebar
2
+ module Avo
3
+ module Concerns
4
+ module VisibleItems
5
+ extend ActiveSupport::Concern
6
+ def items
7
+ items_holder&.items || []
8
+ end
9
+
10
+ def visible_items
11
+ items
12
+ .select do |item|
13
+ if item.respond_to? :hydrate
14
+ item.hydrate(view: view, resource: resource)
15
+ end
16
+
17
+ visible(item)
18
+ end
19
+ end
20
+
21
+ def visible(item)
22
+ return item.visible? if !item.is_field?
23
+
24
+ return false if item.respond_to?(:authorized?) && item.resource.present? && !item.authorized?
25
+
26
+ item.visible? && item.visible_in_view?(view: view)
27
+ end
28
+
29
+ def visible?
30
+ any_item_visible = visible_items.any?
31
+ return any_item_visible if !respond_to?(:visible_on?)
32
+
33
+ visible_on?(view) && any_item_visible
34
+ end
35
+
36
+ def hydrate(view: nil)
37
+ @view = view
38
+
39
+ self
40
+ end
41
+ end
42
+ end
43
+ end
@@ -36,13 +36,14 @@ module Avo
36
36
  attr_accessor :main_menu
37
37
  attr_accessor :profile_menu
38
38
  attr_accessor :model_resource_mapping
39
- attr_accessor :resource_default_view
39
+ attr_reader :resource_default_view
40
40
  attr_accessor :authorization_client
41
41
  attr_accessor :field_wrapper_layout
42
42
  attr_accessor :sign_out_path_name
43
43
  attr_accessor :resources
44
44
  attr_accessor :prefix_path
45
45
  attr_accessor :resource_parent_controller
46
+ attr_accessor :mount_avo_engines
46
47
 
47
48
  def initialize
48
49
  @root_path = "/avo"
@@ -88,11 +89,12 @@ module Avo
88
89
  @main_menu = nil
89
90
  @profile_menu = nil
90
91
  @model_resource_mapping = {}
91
- @resource_default_view = :show
92
+ @resource_default_view = Avo::ViewInquirer.new("show")
92
93
  @authorization_client = :pundit
93
94
  @field_wrapper_layout = :inline
94
95
  @resources = nil
95
96
  @resource_parent_controller = "Avo::ResourcesController"
97
+ @mount_avo_engines = true
96
98
  end
97
99
 
98
100
  def current_user_method(&block)
@@ -158,6 +160,10 @@ module Avo
158
160
  "community"
159
161
  end
160
162
  end
163
+
164
+ def resource_default_view=(view)
165
+ @resource_default_view = Avo::ViewInquirer.new(view.to_s)
166
+ end
161
167
  end
162
168
 
163
169
  def self.configuration
data/lib/avo/current.rb CHANGED
@@ -24,6 +24,7 @@ class Avo::Current < ActiveSupport::CurrentAttributes
24
24
  attribute :resource_manager
25
25
  attribute :tool_manager
26
26
  attribute :plugin_manager
27
+ attribute :locale
27
28
 
28
29
  delegate :params, to: :request
29
30
 
@@ -1,6 +1,8 @@
1
1
  module Avo
2
2
  class ExecutionContext
3
- attr_accessor :target, :context, :params, :view_context, :current_user, :request, :include, :main_app, :avo
3
+ include Avo::Concerns::HasHelpers
4
+
5
+ attr_accessor :target, :context, :params, :view_context, :current_user, :request, :include, :main_app, :avo, :locale
4
6
 
5
7
  def initialize(**args)
6
8
  # Extend the class with custom modules if required.
@@ -24,6 +26,7 @@ module Avo
24
26
  @params ||= Avo::Current.params
25
27
  @request ||= Avo::Current.request
26
28
  @view_context ||= Avo::Current.view_context
29
+ @locale ||= Avo::Current.locale
27
30
  @main_app ||= @view_context.main_app
28
31
  @avo ||= @view_context.avo
29
32
  end
@@ -8,7 +8,7 @@ module Avo
8
8
 
9
9
  hide_on [:edit, :new]
10
10
 
11
- default_options = {info: :info, success: :success, danger: :danger, warning: :warning}
11
+ default_options = {info: :info, success: :success, danger: :danger, warning: :warning, neutral: :neutral}
12
12
  @options = args[:options].present? ? default_options.merge(args[:options]) : default_options
13
13
  end
14
14
  end
@@ -7,6 +7,7 @@ module Avo
7
7
  prepend Avo::Concerns::IsResourceItem
8
8
  include Avo::Concerns::IsVisible
9
9
  include Avo::Concerns::VisibleInDifferentViews
10
+ include Avo::Concerns::HasHelpers
10
11
  include Avo::Fields::Concerns::HasFieldName
11
12
  include Avo::Fields::Concerns::HasDefault
12
13
  include Avo::Fields::Concerns::HasHTMLAttributes
@@ -14,6 +15,7 @@ module Avo
14
15
  include Avo::Fields::Concerns::IsReadonly
15
16
  include Avo::Fields::Concerns::IsDisabled
16
17
  include Avo::Fields::Concerns::IsRequired
18
+ include Avo::Fields::Concerns::UseViewComponents
17
19
 
18
20
  include ActionView::Helpers::UrlHelper
19
21
 
@@ -73,11 +75,12 @@ module Avo
73
75
  @visible = args[:visible]
74
76
  @as_avatar = args[:as_avatar] || false
75
77
  @html = args[:html] || nil
76
- @view = args[:view] || nil
78
+ @view = Avo::ViewInquirer.new(args[:view]) || nil
77
79
  @value = args[:value] || nil
78
80
  @stacked = args[:stacked] || nil
79
81
  @for_presentation_only = args[:for_presentation_only] || false
80
82
  @resource = args[:resource]
83
+ @components = args[:components] || {}
81
84
 
82
85
  @args = args
83
86
 
@@ -155,14 +158,7 @@ module Avo
155
158
 
156
159
  # Run computable callback block if present
157
160
  if computable && block.present?
158
- final_value = Avo::ExecutionContext.new(
159
- target: block,
160
- record: record,
161
- resource: @resource,
162
- view: @view,
163
- field: self,
164
- include: self.class.included_modules
165
- ).handle
161
+ final_value = execute_block
166
162
  end
167
163
 
168
164
  # Run the value through resolver if present
@@ -181,6 +177,17 @@ module Avo
181
177
  final_value
182
178
  end
183
179
 
180
+ def execute_block
181
+ Avo::ExecutionContext.new(
182
+ target: block,
183
+ record: record,
184
+ resource: resource,
185
+ view: view,
186
+ field: self,
187
+ include: self.class.included_modules
188
+ ).handle
189
+ end
190
+
184
191
  # Fills the record with the received value on create and update actions.
185
192
  def fill_field(record, key, value, params)
186
193
  return record unless record.methods.include? key.to_sym
@@ -192,7 +199,8 @@ module Avo
192
199
  key: key,
193
200
  value: value,
194
201
  resource: resource,
195
- field: self
202
+ field: self,
203
+ include: self.class.included_modules
196
204
  ).handle
197
205
  end
198
206
 
@@ -220,35 +228,8 @@ module Avo
220
228
  id.to_sym
221
229
  end
222
230
 
223
- def view_component_name
224
- "#{type.camelize}Field"
225
- end
226
-
227
- # For custom components the namespace will be different than Avo::Fields so we should take that into account.
228
- def view_component_namespace
229
- "#{self.class.to_s.deconstantize}::#{view_component_name}"
230
- end
231
-
232
- # Try and build the component class or fallback to a blank one
233
- def component_for_view(view = :index)
234
- # Use the edit variant for all "update" views
235
- view = :edit if view.in? [:new, :create, :update]
236
-
237
- component_class = "#{view_component_namespace}::#{view.to_s.camelize}Component"
238
- component_class.constantize
239
- rescue
240
- unless Rails.env.test?
241
- Avo.logger.info "Failed to find component for the `#{self.class}` field on the `#{view}` view."
242
- end
243
- # When returning nil, a race condition happens and throws an error in some environments.
244
- # See https://github.com/avo-hq/avo/pull/365
245
- ::Avo::BlankFieldComponent
246
- end
247
-
248
231
  def record_errors
249
- return {} if record.nil?
250
-
251
- record.errors
232
+ record.nil? ? {} : record.errors
252
233
  end
253
234
 
254
235
  def type
@@ -289,11 +270,7 @@ module Avo
289
270
  private
290
271
 
291
272
  def model_or_class(model)
292
- if model.instance_of?(String)
293
- "class"
294
- else
295
- "model"
296
- end
273
+ model.instance_of?(String) ? "class" : "model"
297
274
  end
298
275
 
299
276
  def is_model?(model)
@@ -305,7 +282,7 @@ module Avo
305
282
  end
306
283
 
307
284
  def on_create?
308
- @view.in?([:new, :create])
285
+ @view.in?(%w[new create])
309
286
  end
310
287
 
311
288
  def in_action?
@@ -259,7 +259,7 @@ module Avo
259
259
  end
260
260
 
261
261
  def name
262
- return polymorphic_as.to_s.humanize if polymorphic_as.present? && view == :index
262
+ return polymorphic_as.to_s.humanize if polymorphic_as.present? && view.index?
263
263
 
264
264
  super
265
265
  end
@@ -13,6 +13,8 @@ module Avo
13
13
  # get_html :classes, view: :show, element: :wrapper
14
14
  # get_html :styles, view: :index, element: :wrapper
15
15
  def get_html(name = nil, element:, view:)
16
+ view = view.to_sym if view.present?
17
+
16
18
  if [view, element].any?(&:nil?)
17
19
  default_attribute_value name
18
20
  end
@@ -0,0 +1,45 @@
1
+ module Avo
2
+ module Fields
3
+ module Concerns
4
+ module UseViewComponents
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_reader :components
9
+ end
10
+
11
+ def view_component_name
12
+ "#{type.camelize}Field"
13
+ end
14
+
15
+ # For custom components the namespace will be different than Avo::Fields so we should take that into account.
16
+ def view_component_namespace
17
+ "#{self.class.to_s.deconstantize}::#{view_component_name}"
18
+ end
19
+
20
+ # Try and build the component class or fallback to a blank one
21
+ def component_for_view(view = :index)
22
+ # Use the edit variant for all "update" views
23
+ view = :edit if view.in? [:new, :create, :update]
24
+
25
+ custom_components = Avo::ExecutionContext.new(
26
+ target: components,
27
+ resource: @resource,
28
+ record: @record,
29
+ view: view
30
+ ).handle
31
+
32
+ component_class = custom_components.dig("#{view}_component".to_sym) || "#{view_component_namespace}::#{view.to_s.camelize}Component"
33
+ component_class.to_s.constantize
34
+ rescue
35
+ unless Rails.env.test?
36
+ Avo.logger.info "Failed to find component for the `#{self.class}` field on the `#{view}` view."
37
+ end
38
+ # When returning nil, a race condition happens and throws an error in some environments.
39
+ # See https://github.com/avo-hq/avo/pull/365
40
+ ::Avo::BlankFieldComponent
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -4,12 +4,12 @@ module Avo
4
4
  attr_reader :width
5
5
  attr_reader :height
6
6
  attr_reader :radius
7
- attr_reader :link_to_resource
7
+ attr_reader :link_to_record
8
8
 
9
9
  def initialize(id, **args, &block)
10
10
  super(id, **args, &block)
11
11
 
12
- @link_to_resource = args[:link_to_resource].present? ? args[:link_to_resource] : false
12
+ @link_to_record = args[:link_to_record].present? ? args[:link_to_record] : false
13
13
 
14
14
  @width = args[:width].present? ? args[:width] : 40
15
15
  @height = args[:height].present? ? args[:height] : 40
@@ -1,7 +1,7 @@
1
1
  module Avo
2
2
  module Fields
3
3
  class FileField < BaseField
4
- attr_accessor :link_to_resource
4
+ attr_accessor :link_to_record
5
5
  attr_accessor :is_avatar
6
6
  attr_accessor :is_image
7
7
  attr_accessor :is_audio
@@ -12,7 +12,7 @@ module Avo
12
12
  def initialize(id, **args, &block)
13
13
  super(id, **args, &block)
14
14
 
15
- @link_to_resource = args[:link_to_resource].present? ? args[:link_to_resource] : false
15
+ @link_to_record = args[:link_to_record].present? ? args[:link_to_record] : false
16
16
  @is_avatar = args[:is_avatar].present? ? args[:is_avatar] : false
17
17
  @is_image = args[:is_image].present? ? args[:is_image] : @is_avatar
18
18
  @is_audio = args[:is_audio].present? ? args[:is_audio] : false
@@ -4,7 +4,7 @@ require "erb"
4
4
  module Avo
5
5
  module Fields
6
6
  class GravatarField < BaseField
7
- attr_reader :link_to_resource
7
+ attr_reader :link_to_record
8
8
  attr_reader :rounded
9
9
  attr_reader :size
10
10
  attr_reader :default
@@ -16,7 +16,7 @@ module Avo
16
16
 
17
17
  hide_on [:edit, :new]
18
18
 
19
- @link_to_resource = args[:link_to_resource].present? ? args[:link_to_resource] : false
19
+ @link_to_record = args[:link_to_record].present? ? args[:link_to_record] : false
20
20
  @rounded = args[:rounded].nil? ? true : args[:rounded]
21
21
  @size = args[:size].present? ? args[:size].to_i : 32
22
22
  @default = args[:default].present? ? ERB::Util.url_encode(args[:default]).to_s : ""
@@ -79,8 +79,8 @@ module Avo
79
79
 
80
80
  # Adds the view override component
81
81
  # has_one, has_many, has_and_belongs_to_many fields don't have edit views
82
- def component_for_view(view = :index)
83
- view = :show if view.in? [:new, :create, :update, :edit]
82
+ def component_for_view(view = Avo::ViewInquirer.new("index"))
83
+ view = Avo::ViewInquirer.new("show") if view.in? %w[new create update edit]
84
84
 
85
85
  super view
86
86
  end
@@ -4,35 +4,27 @@ module Avo
4
4
  module Fields
5
5
  class HeadingField < BaseField
6
6
  attr_reader :as_html
7
- attr_reader :empty
8
-
9
- def initialize(content, **args, &block)
10
- # Mark the field as empty if there's no content passed
11
- @empty = content.blank?
12
- # Add dummy content
13
- content ||= SecureRandom.hex
14
7
 
8
+ def initialize(id, **args, &block)
15
9
  args[:updatable] = false
10
+ @label = args[:label] || id.to_s.humanize
16
11
 
17
- super(content, **args, &block)
12
+ super(id, **args, &block)
18
13
 
19
14
  # this field is not used to update anything
20
15
  @for_presentation_only = true
21
16
 
22
17
  hide_on :index
23
18
 
24
- @as_html = args[:as_html].present? ? args[:as_html] : false
19
+ @as_html = args[:as_html].presence || false
25
20
  end
26
21
 
27
22
  def id
28
23
  "heading_#{name.to_s.parameterize.underscore}"
29
24
  end
30
25
 
31
- # Override the value method if the field is empty
32
26
  def value
33
- return nil if empty
34
-
35
- super
27
+ block.present? ? execute_block : @label
36
28
  end
37
29
  end
38
30
  end