avo 2.16.1.pre.1.nativefields → 2.17.1.pre.1.zeitwerk.eager.load.dir

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -3
  3. data/Gemfile.lock +43 -8
  4. data/README.md +1 -1
  5. data/app/assets/config/avo_manifest.js +1 -0
  6. data/app/assets/svgs/placeholder.svg +1 -0
  7. data/app/components/avo/actions_component.html.erb +3 -3
  8. data/app/components/avo/base_component.rb +7 -4
  9. data/app/components/avo/field_wrapper_component.html.erb +8 -10
  10. data/app/components/avo/field_wrapper_component.rb +14 -12
  11. data/app/components/avo/fields/badge_field/index_component.html.erb +1 -1
  12. data/app/components/avo/fields/belongs_to_field/edit_component.rb +9 -3
  13. data/app/components/avo/fields/belongs_to_field/index_component.html.erb +1 -1
  14. data/app/components/avo/fields/belongs_to_field/show_component.html.erb +1 -1
  15. data/app/components/avo/fields/belongs_to_field/show_component.rb +1 -1
  16. data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
  17. data/app/components/avo/fields/boolean_field/show_component.html.erb +1 -1
  18. data/app/components/avo/fields/boolean_group_field/index_component.html.erb +1 -1
  19. data/app/components/avo/fields/boolean_group_field/show_component.html.erb +1 -1
  20. data/app/components/avo/fields/code_field/show_component.html.erb +1 -1
  21. data/app/components/avo/fields/common/heading_component.html.erb +5 -4
  22. data/app/components/avo/fields/common/heading_component.rb +6 -1
  23. data/app/components/avo/fields/country_field/index_component.html.erb +1 -1
  24. data/app/components/avo/fields/date_field/index_component.html.erb +1 -1
  25. data/app/components/avo/fields/date_time_field/index_component.html.erb +1 -1
  26. data/app/components/avo/fields/edit_component.rb +6 -4
  27. data/app/components/avo/fields/external_image_field/index_component.html.erb +1 -1
  28. data/app/components/avo/fields/file_field/edit_component.html.erb +2 -1
  29. data/app/components/avo/fields/file_field/index_component.html.erb +1 -1
  30. data/app/components/avo/fields/files_field/edit_component.html.erb +2 -1
  31. data/app/components/avo/fields/files_field/index_component.html.erb +1 -1
  32. data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
  33. data/app/components/avo/fields/gravatar_field/index_component.html.erb +1 -1
  34. data/app/components/avo/fields/has_one_field/index_component.html.erb +1 -1
  35. data/app/components/avo/fields/heading_field/edit_component.html.erb +1 -1
  36. data/app/components/avo/fields/heading_field/show_component.html.erb +1 -1
  37. data/app/components/avo/fields/id_field/index_component.html.erb +1 -1
  38. data/app/components/avo/fields/index_component.rb +11 -2
  39. data/app/components/avo/fields/number_field/index_component.html.erb +1 -1
  40. data/app/components/avo/fields/progress_bar_field/index_component.html.erb +1 -1
  41. data/app/components/avo/fields/select_field/index_component.html.erb +1 -1
  42. data/app/components/avo/fields/show_component.rb +7 -1
  43. data/app/components/avo/fields/status_field/index_component.html.erb +1 -1
  44. data/app/components/avo/fields/tags_field/index_component.html.erb +1 -1
  45. data/app/components/avo/fields/text_field/edit_component.html.erb +3 -1
  46. data/app/components/avo/fields/text_field/index_component.html.erb +1 -1
  47. data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -1
  48. data/app/components/avo/filters_component.html.erb +2 -2
  49. data/app/components/avo/index/grid_cover_empty_state_component.html.erb +1 -1
  50. data/app/components/avo/index/grid_item_component.html.erb +15 -13
  51. data/app/components/avo/index/grid_item_component.rb +1 -1
  52. data/app/components/avo/index/ordering/buttons_component.html.erb +1 -1
  53. data/app/components/avo/index/resource_controls_component.rb +2 -2
  54. data/app/components/avo/index/table_row_component.html.erb +1 -1
  55. data/app/components/avo/panel_component.html.erb +9 -2
  56. data/app/components/avo/panel_component.rb +3 -4
  57. data/app/components/avo/resource_component.rb +18 -0
  58. data/app/components/avo/resource_sidebar_component.html.erb +19 -0
  59. data/app/components/avo/resource_sidebar_component.rb +26 -0
  60. data/app/components/avo/sidebar_profile_component.html.erb +1 -1
  61. data/app/components/avo/tab_switcher_component.html.erb +2 -2
  62. data/app/components/avo/tab_switcher_component.rb +2 -0
  63. data/app/components/avo/views/resource_edit_component.html.erb +31 -25
  64. data/app/components/avo/views/resource_edit_component.rb +1 -1
  65. data/app/components/avo/views/resource_show_component.html.erb +8 -2
  66. data/app/components/avo/views/resource_show_component.rb +1 -1
  67. data/app/controllers/avo/actions_controller.rb +10 -2
  68. data/app/controllers/avo/application_controller.rb +4 -2
  69. data/app/controllers/avo/associations_controller.rb +24 -5
  70. data/app/controllers/avo/attachments_controller.rb +2 -1
  71. data/app/controllers/avo/base_controller.rb +6 -4
  72. data/app/controllers/avo/search_controller.rb +13 -4
  73. data/app/helpers/avo/application_helper.rb +7 -3
  74. data/app/helpers/avo/resources_helper.rb +2 -2
  75. data/app/javascript/avo.base.js +3 -1
  76. data/app/javascript/js/controllers/action_controller.js +1 -4
  77. data/app/javascript/js/controllers/actions_picker_controller.js +8 -9
  78. data/app/javascript/js/controllers/tabs_controller.js +14 -27
  79. data/app/views/avo/actions/show.html.erb +2 -2
  80. data/app/views/avo/home/failed_to_load.html.erb +3 -2
  81. data/avo.gemspec +2 -2
  82. data/config/brakeman.ignore +40 -0
  83. data/config/i18n-tasks.yml +159 -0
  84. data/config/routes.rb +1 -1
  85. data/db/factories.rb +20 -0
  86. data/lib/avo/app.rb +6 -0
  87. data/lib/avo/base_resource.rb +26 -0
  88. data/lib/avo/concerns/fetches_things.rb +1 -1
  89. data/lib/avo/concerns/has_fields.rb +22 -0
  90. data/lib/avo/concerns/is_resource_item.rb +4 -0
  91. data/lib/avo/configuration/branding.rb +9 -1
  92. data/lib/avo/dynamic_router.rb +19 -15
  93. data/lib/avo/engine.rb +9 -0
  94. data/lib/avo/fields/belongs_to_field.rb +3 -0
  95. data/lib/avo/fields/heading_field.rb +15 -0
  96. data/lib/avo/items_holder.rb +4 -0
  97. data/lib/avo/licensing/pro_license.rb +1 -0
  98. data/lib/avo/menu/builder.rb +1 -1
  99. data/lib/avo/menu/menu.rb +0 -2
  100. data/lib/avo/reloader.rb +27 -26
  101. data/lib/avo/services/authorization_service.rb +17 -3
  102. data/lib/avo/services/encryption_service.rb +1 -1
  103. data/lib/avo/sidebar.rb +60 -0
  104. data/lib/avo/sidebar_builder.rb +24 -0
  105. data/lib/avo/version.rb +1 -1
  106. data/lib/generators/avo/templates/field/components/index_component.html.erb.tt +1 -1
  107. data/lib/generators/avo/templates/initializer/avo.tt +2 -1
  108. data/lib/generators/avo/templates/locales/avo.en.yml +107 -107
  109. data/lib/generators/avo/templates/locales/avo.fr.yml +107 -107
  110. data/lib/generators/avo/templates/locales/avo.nb.yml +119 -0
  111. data/lib/generators/avo/templates/locales/avo.nn.yml +119 -0
  112. data/lib/generators/avo/templates/locales/avo.pt-BR.yml +109 -84
  113. data/lib/generators/avo/templates/locales/avo.ro.yml +109 -81
  114. data/lib/generators/avo/templates/locales/avo.tr.yml +119 -0
  115. data/public/avo-assets/avo.base.css +67 -79
  116. data/public/avo-assets/avo.base.js +63 -63
  117. data/public/avo-assets/avo.base.js.map +3 -3
  118. metadata +16 -22
  119. data/app/views/avo/home/failed_to_load.html copy.erb +0 -23
  120. data/lib/generators/avo/templates/locales/avo.nb-NO.yml +0 -93
@@ -101,6 +101,32 @@ module Avo
101
101
 
102
102
  ordering.dig(:actions) || {}
103
103
  end
104
+
105
+ def get_record_associations(record)
106
+ record._reflections
107
+ end
108
+
109
+ def valid_association_name(record, association_name)
110
+ get_record_associations(record).keys.find do |name|
111
+ name == association_name
112
+ end
113
+ end
114
+
115
+ def valid_attachment_name(record, association_name)
116
+ get_record_associations(record).keys.each do |name|
117
+ return association_name if name == "#{association_name}_attachment" || name == "#{association_name}_attachments"
118
+ end
119
+ end
120
+
121
+ def get_available_models
122
+ ApplicationRecord.descendants
123
+ end
124
+
125
+ def valid_model_class(model_class)
126
+ get_available_models.find do |m|
127
+ m.to_s == model_class.to_s
128
+ end
129
+ end
104
130
  end
105
131
 
106
132
  def initialize
@@ -36,7 +36,7 @@ module Avo
36
36
  possible_resource = "#{resource}Resource".gsub "ResourceResource", "Resource"
37
37
 
38
38
  resources.find do |available_resource|
39
- possible_resource.safe_constantize == available_resource.class
39
+ possible_resource.to_s == available_resource.class.to_s
40
40
  end
41
41
  end
42
42
 
@@ -43,6 +43,12 @@ module Avo
43
43
 
44
44
  items_holder.heading body, **args
45
45
  end
46
+
47
+ def sidebar(**args, &block)
48
+ ensure_items_holder_initialized
49
+
50
+ items_holder.sidebar Avo::SidebarBuilder.parse_block(**args, &block)
51
+ end
46
52
  # END DSL methods
47
53
 
48
54
  def items
@@ -76,6 +82,11 @@ module Avo
76
82
  fields << extract_fields_from_items(tab)
77
83
  end
78
84
  end
85
+
86
+ # Dive into sidebar to fetch their fields
87
+ if item.is_sidebar?
88
+ fields << extract_fields_from_items(item)
89
+ end
79
90
  end
80
91
 
81
92
  if item.is_field?
@@ -277,6 +288,17 @@ module Avo
277
288
  end
278
289
  end
279
290
 
291
+ # Make sure all tabs panelfull_items are setted as inside tabs
292
+ panelfull_items.grep(Avo::TabGroup).each do |tab_group|
293
+ tab_group.items.grep(Avo::Tab).each do |tab|
294
+ tab.items.grep(Avo::Panel).each do |panel|
295
+ panel.items.grep(Avo::Fields::BelongsToField).each do |field|
296
+ field.target = :_top
297
+ end
298
+ end
299
+ end
300
+ end
301
+
280
302
  # Add all the panelles fields to a new panel
281
303
  main_panel_holder = Avo::ItemsHolder.new
282
304
  main_panel_holder.items = panelless_items
@@ -31,6 +31,10 @@ module Avo
31
31
  def is_tab_group?
32
32
  self.class.item_type == :tab_group
33
33
  end
34
+
35
+ def is_sidebar?
36
+ self.class.item_type == :sidebar
37
+ end
34
38
  end
35
39
  end
36
40
  end
@@ -1,9 +1,10 @@
1
1
  class Avo::Configuration::Branding
2
- def initialize(colors: nil, chart_colors: nil, logo: nil, logomark: nil)
2
+ def initialize(colors: nil, chart_colors: nil, logo: nil, logomark: nil, placeholder: nil)
3
3
  @colors = colors || {}
4
4
  @chart_colors = chart_colors
5
5
  @logo = logo
6
6
  @logomark = logomark
7
+ @placeholder = placeholder
7
8
 
8
9
  @default_colors = {
9
10
  background: "#F6F6F7",
@@ -15,6 +16,7 @@ class Avo::Configuration::Branding
15
16
  @default_chart_colors = ["#0B8AE2", "#34C683", "#2AB1EE", "#34C6A8"]
16
17
  @default_logo = "/avo-assets/logo.png"
17
18
  @default_logomark = "/avo-assets/logomark.png"
19
+ @default_placeholder = "placeholder.svg"
18
20
  end
19
21
 
20
22
  def css_colors
@@ -39,6 +41,12 @@ class Avo::Configuration::Branding
39
41
  @logomark || @default_logomark
40
42
  end
41
43
 
44
+ def placeholder
45
+ return @default_placeholder if Avo::App.license.lacks_with_trial(:branding)
46
+
47
+ @placeholder || @default_placeholder
48
+ end
49
+
42
50
  def chart_colors
43
51
  return @default_chart_colors if Avo::App.license.lacks_with_trial(:branding)
44
52
 
@@ -1,21 +1,25 @@
1
1
  module Avo
2
- module DynamicRouter
3
- def self.routes(router)
4
- Rails.application.eager_load! unless Rails.env.production?
2
+ class DynamicRouter
3
+ def self.routes
4
+ Avo::Engine.routes.draw do
5
+ scope "resources", as: "resources" do
6
+ Avo::App.eager_load_resources
5
7
 
6
- BaseResource.descendants
7
- .select do |resource|
8
- resource != :BaseResource
9
- end
10
- .select do |resource|
11
- resource.is_a? Class
12
- end
13
- # .select do |resource|
14
- # resource.model_class.present?
15
- # end
16
- .map do |resource|
17
- router.resources resource.new.route_key
8
+ BaseResource.descendants
9
+ .select do |resource|
10
+ resource != :BaseResource
11
+ end
12
+ .select do |resource|
13
+ resource.is_a? Class
14
+ end
15
+ # .select do |resource|
16
+ # resource.model_class.present?
17
+ # end
18
+ .map do |resource|
19
+ resources resource.new.route_key
20
+ end
18
21
  end
22
+ end
19
23
  end
20
24
  end
21
25
  end
data/lib/avo/engine.rb CHANGED
@@ -17,6 +17,15 @@ module Avo
17
17
  ::Avo::App.boot
18
18
  end
19
19
 
20
+ # initializer "eager load resources" do |app|
21
+ # # puts ["app.root->", app.root.join('app', 'avo', 'resources')].inspect
22
+ # app.config.to_prepare do
23
+ # puts ["app.config.to_prepare->", app.root.join('app', 'avo', 'resources')].inspect
24
+ # Rails.autoloaders.main.eager_load_dir(app.root.join('app', 'avo', 'resources'))
25
+ # puts [".to_prepare BaseResource.descendants->", BaseResource.descendants].inspect
26
+ # end
27
+ # end
28
+
20
29
  initializer "avo.autoload" do |app|
21
30
  [
22
31
  ["app", "avo", "fields"],
@@ -58,6 +58,8 @@ module Avo
58
58
  # - is_disabled?
59
59
 
60
60
  class BelongsToField < BaseField
61
+ attr_accessor :target
62
+
61
63
  attr_reader :polymorphic_as
62
64
  attr_reader :relation_method
63
65
  attr_reader :types # for Polymorphic associations
@@ -77,6 +79,7 @@ module Avo
77
79
  @allow_via_detaching = args[:allow_via_detaching] == true
78
80
  @attach_scope = args[:attach_scope]
79
81
  @polymorphic_help = args[:polymorphic_help]
82
+ @target = args[:target]
80
83
  end
81
84
 
82
85
  def searchable
@@ -1,9 +1,17 @@
1
+ require 'securerandom'
2
+
1
3
  module Avo
2
4
  module Fields
3
5
  class HeadingField < BaseField
4
6
  attr_reader :as_html
7
+ attr_reader :empty
5
8
 
6
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
15
  args[:updatable] = false
8
16
 
9
17
  super(content, **args, &block)
@@ -16,6 +24,13 @@ module Avo
16
24
  def id
17
25
  "heading_#{name.to_s.parameterize.underscore}"
18
26
  end
27
+
28
+ # Override the value method if the field is empty
29
+ def value
30
+ return nil if empty
31
+
32
+ super
33
+ end
19
34
  end
20
35
  end
21
36
  end
@@ -55,6 +55,10 @@ module Avo
55
55
  add_item field
56
56
  end
57
57
 
58
+ def sidebar(instance)
59
+ add_item instance
60
+ end
61
+
58
62
  def add_item(instance)
59
63
  @items << instance
60
64
 
@@ -17,6 +17,7 @@ module Avo
17
17
  :stimulus_js_integration,
18
18
  :resource_show_controls,
19
19
  :branding,
20
+ :resource_sidebar,
20
21
  :advanced_fields
21
22
  ]
22
23
  end
@@ -57,7 +57,7 @@ class Avo::Menu::Builder
57
57
  # Add all the resources
58
58
  def all_resources(**args)
59
59
  Avo::App.resources_for_navigation.each do |res|
60
- resource res.model_class, **args
60
+ resource res.route_key, **args
61
61
  end
62
62
  end
63
63
 
data/lib/avo/menu/menu.rb CHANGED
@@ -1,4 +1,2 @@
1
1
  class Avo::Menu::Menu < OpenStruct
2
-
3
-
4
2
  end
data/lib/avo/reloader.rb CHANGED
@@ -20,40 +20,41 @@ class Avo::Reloader
20
20
  end
21
21
 
22
22
  private
23
- def updater
24
- @updater ||= config.file_watcher.new(files, directories) { reload! }
25
- end
26
23
 
27
- def files
28
- # we want to watch some files no matter what
29
- paths = [
30
- Rails.root.join("config", "initializers", "avo.rb"),
31
- ]
24
+ def updater
25
+ @updater ||= config.file_watcher.new(files, directories) { reload! }
26
+ end
32
27
 
33
- # we want to watch some files only in Avo development
34
- if reload_lib?
35
- paths += []
36
- end
28
+ def files
29
+ # we want to watch some files no matter what
30
+ paths = [
31
+ Rails.root.join("config", "initializers", "avo.rb"),
32
+ ]
37
33
 
38
- paths
34
+ # we want to watch some files only in Avo development
35
+ if reload_lib?
36
+ paths += []
39
37
  end
40
38
 
41
- def directories
42
- dirs = {}
39
+ paths
40
+ end
43
41
 
44
- # watch the lib directory in Avo development
45
- if reload_lib?
46
- dirs[Avo::Engine.root.join("lib", "avo").to_s] = ["rb"]
47
- end
42
+ def directories
43
+ dirs = {}
48
44
 
49
- dirs
45
+ # watch the lib directory in Avo development
46
+ if reload_lib?
47
+ dirs[Avo::Engine.root.join("lib", "avo").to_s] = ["rb"]
50
48
  end
51
49
 
52
- def config
53
- Rails.application.config
54
- end
50
+ dirs
51
+ end
55
52
 
56
- def reload_lib?
57
- Avo::IN_DEVELOPMENT || ENV['AVO_RELOAD_LIB_DIR']
58
- end
53
+ def config
54
+ Rails.application.config
55
+ end
56
+
57
+ def reload_lib?
58
+ Avo::IN_DEVELOPMENT || ENV["AVO_RELOAD_LIB_DIR"]
59
+ end
59
60
  end
@@ -49,10 +49,15 @@ module Avo
49
49
  return model if skip_authorization || user.nil?
50
50
 
51
51
  begin
52
- if policy_class
53
- policy_class::Scope.new(user, model).resolve
52
+ # Try and figure out the scope from a given policy or auto-detected one
53
+ scope_from_policy_class = scope_for_policy_class(policy_class)
54
+
55
+ # If we discover one use it.
56
+ # Else fallback to pundit.
57
+ if scope_from_policy_class.present?
58
+ scope_from_policy_class.new(user, model).resolve
54
59
  else
55
- Pundit.policy_scope! user, model
60
+ Pundit.policy_scope!(user, model)
56
61
  end
57
62
  rescue Pundit::NotDefinedError => e
58
63
  return model unless Avo.configuration.raise_error_on_missing_policy
@@ -88,6 +93,15 @@ module Avo
88
93
  raise error
89
94
  end
90
95
  end
96
+
97
+ # Fetches the scope for a given policy
98
+ def scope_for_policy_class(policy_class = nil)
99
+ return if policy_class.blank?
100
+
101
+ if policy_class.present? && defined?(policy_class::Scope)
102
+ policy_class::Scope
103
+ end
104
+ end
91
105
  end
92
106
 
93
107
  def initialize(user = nil, record = nil, policy_class: nil)
@@ -40,7 +40,7 @@ module Avo
40
40
  end
41
41
 
42
42
  def secret_key_base
43
- ENV['SECRET_KEY_BASE'] || Rails.application.credentials.secret_key_base || Rails.application.secrets.secret_key_base
43
+ ENV["SECRET_KEY_BASE"] || Rails.application.credentials.secret_key_base || Rails.application.secrets.secret_key_base
44
44
  end
45
45
  end
46
46
  end
@@ -0,0 +1,60 @@
1
+ class Avo::Sidebar
2
+ include Avo::Concerns::IsResourceItem
3
+ include Avo::Fields::FieldExtensions::VisibleInDifferentViews
4
+
5
+ class_attribute :item_type, default: :sidebar
6
+ delegate :items, :add_item, to: :items_holder
7
+
8
+ attr_reader :name
9
+ attr_reader :view
10
+ attr_accessor :items_holder
11
+
12
+ def initialize(name: nil, view: nil, **args)
13
+ # Initialize the visibility markers
14
+ super
15
+
16
+ @name = name
17
+ @items_holder = Avo::ItemsHolder.new
18
+ @view = view
19
+
20
+ show_on args[:show_on] if args[:show_on].present?
21
+ hide_on args[:hide_on] if args[:hide_on].present?
22
+ only_on args[:only_on] if args[:only_on].present?
23
+ except_on args[:except_on] if args[:except_on].present?
24
+ end
25
+
26
+ def hydrate(view: nil)
27
+ @view = view
28
+
29
+ self
30
+ end
31
+
32
+ def empty?
33
+ visible_items.blank?
34
+ end
35
+
36
+ def items
37
+ if self.items_holder.present?
38
+ self.items_holder.items
39
+ else
40
+ []
41
+ end
42
+ end
43
+
44
+ def visible_items
45
+ items.map do |item|
46
+ # Remove the fields that shouldn't be visible in this view
47
+ # eg: has_many fields on edit
48
+ if not_visible_field(item)
49
+ nil
50
+ else
51
+ item
52
+ end
53
+ end
54
+ .compact
55
+ end
56
+
57
+ def not_visible_field(item)
58
+ item.is_field? && !item.visible_on?(view)
59
+ end
60
+ end
@@ -0,0 +1,24 @@
1
+ class Avo::SidebarBuilder
2
+ class << self
3
+ def parse_block(**args, &block)
4
+ Docile.dsl_eval(new(**args), &block).build
5
+ end
6
+ end
7
+
8
+ attr_reader :items_holder
9
+
10
+ delegate :field, to: :items_holder
11
+ delegate :items, to: :items_holder
12
+ delegate :heading, to: :items_holder
13
+
14
+ def initialize(name: nil, **args)
15
+ @sidebar = Avo::Sidebar.new(**args)
16
+ @items_holder = Avo::ItemsHolder.new
17
+ end
18
+
19
+ # Fetch the sidebar
20
+ def build
21
+ @sidebar.items_holder = @items_holder
22
+ @sidebar
23
+ end
24
+ end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.16.1.pre.1.nativefields" unless const_defined?(:VERSION)
2
+ VERSION = "2.17.1.pre.1.zeitwerk.eager.load.dir" unless const_defined?(:VERSION)
3
3
  end
@@ -1,3 +1,3 @@
1
- <%%= index_field_wrapper field: @field, resource: @resource do %>
1
+ <%%= index_field_wrapper **field_wrapper_args do %>
2
2
  <%%= @field.value %>
3
3
  <%% end %>
@@ -35,7 +35,7 @@ Avo.configure do |config|
35
35
  # config.locale = 'en-US'
36
36
 
37
37
  ## == Resource options ==
38
- # config.resource_controls = :right
38
+ # config.resource_controls_placement = :right
39
39
  # config.model_resource_mapping = {}
40
40
  # config.default_view_type = :table
41
41
  # config.per_page = 24
@@ -71,6 +71,7 @@ Avo.configure do |config|
71
71
  # chart_colors: ["#0B8AE2", "#34C683", "#2AB1EE", "#34C6A8"],
72
72
  # logo: "/avo-assets/logo.png",
73
73
  # logomark: "/avo-assets/logomark.png"
74
+ # placeholder: "/avo-assets/placeholder.svg"
74
75
  # }
75
76
 
76
77
  ## == Breadcrumbs ==