avo 2.7.0 → 2.9.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 (174) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +6 -4
  4. data/README.md +11 -0
  5. data/app/assets/stylesheets/avo.css +4 -4
  6. data/app/assets/stylesheets/css/{components → fields}/code.css +0 -0
  7. data/app/assets/stylesheets/css/{components → fields}/progress.css +0 -0
  8. data/app/assets/stylesheets/css/{components → fields}/status.css +0 -0
  9. data/app/assets/stylesheets/css/fields/trix.css +17 -0
  10. data/app/assets/svgs/download-solid-reversed.svg +2 -2
  11. data/app/components/avo/actions_component.html.erb +5 -13
  12. data/app/components/avo/actions_component.rb +39 -1
  13. data/app/components/avo/alert_component.rb +6 -0
  14. data/app/components/avo/card_component.html.erb +2 -2
  15. data/app/components/avo/common_field_wrapper_component.html.erb +11 -4
  16. data/app/components/avo/common_field_wrapper_component.rb +27 -1
  17. data/app/components/avo/edit/field_wrapper_component.html.erb +1 -1
  18. data/app/components/avo/fields/badge_field/index_component.html.erb +1 -1
  19. data/app/components/avo/fields/badge_field/show_component.html.erb +1 -1
  20. data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +21 -10
  21. data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +7 -1
  22. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +27 -15
  23. data/app/components/avo/fields/belongs_to_field/edit_component.rb +4 -0
  24. data/app/components/avo/fields/belongs_to_field/index_component.html.erb +1 -1
  25. data/app/components/avo/fields/belongs_to_field/show_component.html.erb +1 -1
  26. data/app/components/avo/fields/boolean_field/edit_component.html.erb +4 -2
  27. data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
  28. data/app/components/avo/fields/boolean_field/show_component.html.erb +1 -1
  29. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +7 -1
  30. data/app/components/avo/fields/boolean_group_field/index_component.html.erb +1 -1
  31. data/app/components/avo/fields/boolean_group_field/show_component.html.erb +1 -1
  32. data/app/components/avo/fields/code_field/edit_component.html.erb +7 -5
  33. data/app/components/avo/fields/code_field/show_component.html.erb +2 -2
  34. data/app/components/avo/fields/common/key_value_component.html.erb +10 -4
  35. data/app/components/avo/fields/common/key_value_component.rb +2 -0
  36. data/app/components/avo/fields/country_field/edit_component.html.erb +4 -2
  37. data/app/components/avo/fields/country_field/index_component.html.erb +1 -1
  38. data/app/components/avo/fields/country_field/show_component.html.erb +1 -1
  39. data/app/components/avo/fields/date_field/edit_component.html.erb +6 -4
  40. data/app/components/avo/fields/date_field/index_component.html.erb +1 -1
  41. data/app/components/avo/fields/date_field/show_component.html.erb +1 -1
  42. data/app/components/avo/fields/date_time_field/edit_component.html.erb +6 -4
  43. data/app/components/avo/fields/date_time_field/index_component.html.erb +1 -1
  44. data/app/components/avo/fields/date_time_field/show_component.html.erb +1 -1
  45. data/app/components/avo/fields/edit_component.rb +7 -0
  46. data/app/components/avo/fields/external_image_field/edit_component.html.erb +5 -2
  47. data/app/components/avo/fields/external_image_field/index_component.html.erb +6 -4
  48. data/app/components/avo/fields/external_image_field/show_component.html.erb +1 -1
  49. data/app/components/avo/fields/file_field/edit_component.html.erb +6 -1
  50. data/app/components/avo/fields/file_field/index_component.html.erb +1 -1
  51. data/app/components/avo/fields/file_field/show_component.html.erb +1 -1
  52. data/app/components/avo/fields/files_field/edit_component.html.erb +7 -1
  53. data/app/components/avo/fields/files_field/index_component.html.erb +1 -1
  54. data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
  55. data/app/components/avo/fields/gravatar_field/index_component.html.erb +1 -1
  56. data/app/components/avo/fields/gravatar_field/show_component.html.erb +1 -1
  57. data/app/components/avo/fields/has_one_field/index_component.html.erb +1 -1
  58. data/app/components/avo/fields/hidden_field/edit_component.html.erb +5 -1
  59. data/app/components/avo/fields/id_field/edit_component.html.erb +1 -1
  60. data/app/components/avo/fields/id_field/index_component.html.erb +1 -1
  61. data/app/components/avo/fields/id_field/show_component.html.erb +1 -1
  62. data/app/components/avo/fields/index_component.rb +3 -0
  63. data/app/components/avo/fields/key_value_field/edit_component.html.erb +1 -1
  64. data/app/components/avo/fields/key_value_field/show_component.html.erb +1 -1
  65. data/app/components/avo/fields/markdown_field/edit_component.html.erb +8 -5
  66. data/app/components/avo/fields/markdown_field/show_component.html.erb +1 -1
  67. data/app/components/avo/fields/number_field/edit_component.html.erb +7 -4
  68. data/app/components/avo/fields/number_field/index_component.html.erb +1 -1
  69. data/app/components/avo/fields/number_field/show_component.html.erb +1 -1
  70. data/app/components/avo/fields/password_field/edit_component.html.erb +4 -2
  71. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +7 -4
  72. data/app/components/avo/fields/progress_bar_field/index_component.html.erb +1 -1
  73. data/app/components/avo/fields/progress_bar_field/show_component.html.erb +1 -1
  74. data/app/components/avo/fields/select_field/edit_component.html.erb +9 -3
  75. data/app/components/avo/fields/select_field/index_component.html.erb +1 -1
  76. data/app/components/avo/fields/select_field/show_component.html.erb +1 -1
  77. data/app/components/avo/fields/show_component.rb +3 -0
  78. data/app/components/avo/fields/status_field/edit_component.html.erb +6 -3
  79. data/app/components/avo/fields/status_field/index_component.html.erb +1 -1
  80. data/app/components/avo/fields/status_field/show_component.html.erb +1 -1
  81. data/app/components/avo/fields/tags_field/edit_component.html.erb +19 -11
  82. data/app/components/avo/fields/tags_field/index_component.html.erb +1 -1
  83. data/app/components/avo/fields/tags_field/show_component.html.erb +1 -1
  84. data/app/components/avo/fields/text_field/edit_component.html.erb +5 -2
  85. data/app/components/avo/fields/text_field/index_component.html.erb +1 -1
  86. data/app/components/avo/fields/text_field/show_component.html.erb +1 -1
  87. data/app/components/avo/fields/textarea_field/edit_component.html.erb +6 -3
  88. data/app/components/avo/fields/textarea_field/show_component.html.erb +1 -1
  89. data/app/components/avo/fields/trix_field/edit_component.html.erb +14 -4
  90. data/app/components/avo/fields/trix_field/edit_component.rb +3 -0
  91. data/app/components/avo/fields/trix_field/show_component.html.erb +2 -2
  92. data/app/components/avo/index/field_wrapper_component.html.erb +12 -5
  93. data/app/components/avo/index/field_wrapper_component.rb +27 -3
  94. data/app/components/avo/panel_component.rb +4 -3
  95. data/app/components/avo/resource_component.rb +1 -0
  96. data/app/components/avo/show/field_wrapper_component.html.erb +1 -1
  97. data/app/components/avo/show/field_wrapper_component.rb +2 -1
  98. data/app/components/avo/sidebar/item_switcher_component.html.erb +2 -2
  99. data/app/components/avo/views/resource_edit_component.html.erb +13 -8
  100. data/app/components/avo/views/resource_edit_component.rb +32 -3
  101. data/app/components/avo/views/resource_index_component.html.erb +7 -4
  102. data/app/components/avo/views/resource_index_component.rb +7 -1
  103. data/app/components/avo/views/resource_show_component.html.erb +11 -9
  104. data/app/components/avo/views/resource_show_component.rb +1 -0
  105. data/app/controllers/avo/actions_controller.rb +4 -1
  106. data/app/controllers/avo/base_controller.rb +24 -13
  107. data/app/controllers/avo/cards_controller.rb +25 -0
  108. data/app/controllers/avo/dashboards_controller.rb +2 -8
  109. data/app/controllers/avo/home_controller.rb +8 -1
  110. data/app/controllers/avo/search_controller.rb +7 -1
  111. data/app/helpers/avo/url_helpers.rb +8 -9
  112. data/app/javascript/js/controllers/fields/code_field_controller.js +7 -1
  113. data/app/javascript/js/controllers/fields/key_value_controller.js +1 -0
  114. data/app/javascript/js/controllers/fields/tags_field_controller.js +0 -1
  115. data/app/javascript/js/controllers/menu_controller.js +4 -3
  116. data/app/javascript/js/controllers/resource_edit_controller.js +72 -0
  117. data/app/javascript/js/controllers/resource_index_controller.js +4 -0
  118. data/app/javascript/js/controllers/resource_show_controller.js +4 -0
  119. data/app/javascript/js/controllers/search_controller.js +28 -5
  120. data/app/javascript/js/controllers.js +8 -0
  121. data/app/views/avo/associations/new.html.erb +2 -1
  122. data/app/views/avo/base/_select_filter.html.erb +1 -1
  123. data/app/views/avo/base/_text_filter.html.erb +1 -0
  124. data/app/views/avo/base/edit.html.erb +2 -1
  125. data/app/views/avo/base/new.html.erb +1 -1
  126. data/app/views/avo/{dashboards → cards}/_chartkick_card.html.erb +0 -0
  127. data/app/views/avo/{dashboards → cards}/_metric_card.html.erb +0 -0
  128. data/app/views/avo/{dashboards/card.html.erb → cards/show.html.erb} +0 -0
  129. data/app/views/avo/partials/_custom_tools_alert.html.erb +21 -7
  130. data/app/views/avo/partials/_logo.html.erb +3 -2
  131. data/app/views/avo/partials/_navbar.html.erb +1 -1
  132. data/app/views/avo/partials/_table_header.html.erb +9 -1
  133. data/bin/test +1 -0
  134. data/config/routes.rb +7 -4
  135. data/db/factories.rb +1 -0
  136. data/lib/avo/app.rb +18 -1
  137. data/lib/avo/base_action.rb +16 -4
  138. data/lib/avo/base_card.rb +0 -23
  139. data/lib/avo/base_resource.rb +23 -16
  140. data/lib/avo/concerns/fetches_things.rb +19 -12
  141. data/lib/avo/concerns/has_fields.rb +93 -0
  142. data/lib/avo/concerns/has_html_attributes.rb +110 -0
  143. data/lib/avo/concerns/has_model.rb +11 -0
  144. data/lib/avo/concerns/has_stimulus_controllers.rb +42 -0
  145. data/lib/avo/dynamic_router.rb +1 -1
  146. data/lib/avo/engine.rb +1 -3
  147. data/lib/avo/fields/base_field.rb +24 -13
  148. data/lib/avo/fields/concerns/is_required.rb +17 -0
  149. data/lib/avo/fields/select_field.rb +1 -1
  150. data/lib/avo/grid_collector.rb +4 -4
  151. data/lib/avo/hosts/view_record_host.rb +7 -0
  152. data/lib/avo/html/builder.rb +117 -0
  153. data/lib/avo/licensing/pro_license.rb +1 -0
  154. data/lib/avo/menu/base_item.rb +4 -0
  155. data/lib/avo/menu/dashboard.rb +5 -0
  156. data/lib/avo/menu/resource.rb +5 -0
  157. data/lib/avo/version.rb +1 -1
  158. data/lib/avo.rb +5 -0
  159. data/lib/generators/avo/install_generator.rb +1 -4
  160. data/lib/generators/avo/templates/cards/chartkick_card_sample.tt +11 -1
  161. data/lib/generators/avo/templates/cards/metric_card_sample.tt +11 -1
  162. data/lib/generators/avo/templates/field/components/edit_component.html.erb.tt +1 -1
  163. data/lib/generators/avo/templates/field/components/index_component.html.erb.tt +1 -1
  164. data/lib/generators/avo/templates/field/components/show_component.html.erb.tt +1 -1
  165. data/lib/generators/avo/templates/initializer/avo.tt +1 -1
  166. data/lib/generators/avo/templates/locales/avo.en.yml +3 -3
  167. data/public/avo-assets/avo.css +473 -1055
  168. data/public/avo-assets/avo.js +147 -147
  169. data/public/avo-assets/avo.js.map +3 -3
  170. data/public/avo-assets/logomark.png +0 -0
  171. metadata +21 -11
  172. data/app/components/avo/views/resource_new_component.html.erb +0 -55
  173. data/app/components/avo/views/resource_new_component.rb +0 -38
  174. data/lib/avo/fields_collector.rb +0 -70
@@ -22,6 +22,13 @@ module Avo
22
22
  end
23
23
  end
24
24
 
25
+ # Filters out the resources that are missing the model_class
26
+ def valid_resources
27
+ resources.select do |resource|
28
+ resource.model_class.present?
29
+ end
30
+ end
31
+
25
32
  # Returns the Avo resource by camelized name
26
33
  #
27
34
  # get_resource_by_name('User') => UserResource
@@ -45,9 +52,10 @@ module Avo
45
52
  # get_resource_by_name('User') => UserResource
46
53
  # get_resource_by_name(User) => UserResource
47
54
  def get_resource_by_model_name(name)
48
- resources.find do |resource|
49
- resource.model_class.model_name.name == name.to_s
50
- end
55
+ valid_resources
56
+ .find do |resource|
57
+ resource.model_class.model_name.name == name.to_s
58
+ end
51
59
  end
52
60
 
53
61
  # Returns the Avo resource by singular snake_cased name
@@ -55,9 +63,10 @@ module Avo
55
63
  # get_resource_by_controller_name('delayed_backend_active_record_jobs') => DelayedJobResource
56
64
  # get_resource_by_controller_name('users') => UserResource
57
65
  def get_resource_by_controller_name(name)
58
- resources.find do |resource|
59
- resource.model_class.to_s.pluralize.underscore.tr("/", "_") == name.to_s
60
- end
66
+ valid_resources
67
+ .find do |resource|
68
+ resource.model_class.to_s.pluralize.underscore.tr("/", "_") == name.to_s
69
+ end
61
70
  end
62
71
 
63
72
  # Returns the Avo resource by some name
@@ -73,9 +82,10 @@ module Avo
73
82
  end
74
83
 
75
84
  def get_available_resources(user = nil)
76
- resources.select do |resource|
77
- Services::AuthorizationService.authorize user, resource.model_class, Avo.configuration.authorization_methods.stringify_keys["index"], raise_exception: false
78
- end
85
+ valid_resources
86
+ .select do |resource|
87
+ Services::AuthorizationService.authorize user, resource.model_class, Avo.configuration.authorization_methods.stringify_keys["index"], raise_exception: false
88
+ end
79
89
  .sort_by { |r| r.name }
80
90
  end
81
91
 
@@ -85,9 +95,6 @@ module Avo
85
95
 
86
96
  def resources_for_navigation(user = nil)
87
97
  get_available_resources(current_user)
88
- .select do |resource|
89
- resource.model_class.present?
90
- end
91
98
  .select do |resource|
92
99
  resource.visible_on_sidebar
93
100
  end
@@ -0,0 +1,93 @@
1
+ module Avo
2
+ module Concerns
3
+ module HasFields
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :fields
8
+ class_attribute :fields_index, default: 0
9
+ end
10
+
11
+ class_methods do
12
+ def field(field_name, as:, **args, &block)
13
+ self.invalid_fields ||= []
14
+
15
+ field_instance = parse_field(field_name, as: as, order_index: fields_index, **args, &block)
16
+
17
+ if field_instance.present?
18
+ add_to_fields field_instance
19
+ 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
+ })
26
+ end
27
+ end
28
+
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
35
+ end
36
+ end
37
+
38
+ def heading(body, **args)
39
+ add_to_fields Avo::Fields::HeadingField.new(body, order_index: fields_index, **args)
40
+ end
41
+
42
+ private
43
+
44
+ def add_to_fields(instance)
45
+ self.fields ||= []
46
+ self.fields << instance
47
+ self.fields_index += 1
48
+ end
49
+
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
56
+ end
57
+
58
+ def field_class_from_symbol(symbol)
59
+ matched_field = Avo::App.fields.find do |field|
60
+ field[:name].to_s == symbol.to_s
61
+ end
62
+
63
+ return matched_field[:class] if matched_field.present? && matched_field[:class].present?
64
+ end
65
+
66
+ def parse_symbol(field_name, as:, **args, &block)
67
+ field_class = field_class_from_symbol(as)
68
+
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"
76
+
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)
80
+ end
81
+ end
82
+ end
83
+
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)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,110 @@
1
+ module Avo
2
+ module Concerns
3
+ module HasHTMLAttributes
4
+ extend ActiveSupport::Concern
5
+
6
+ attr_reader :html
7
+
8
+ # Used to get attributes for elements and views
9
+ #
10
+ # examples:
11
+ # get_html :data, view: :edit, element: :input
12
+ # get_html :classes, view: :show, element: :wrapper
13
+ # get_html :styles, view: :index, element: :wrapper
14
+ def get_html(name = nil, element:, view:)
15
+ if [view, element].any?(&:nil?) || Avo::App.license.lacks_with_trial(:stimulus_js_integration)
16
+ default_attribute_value name
17
+ end
18
+
19
+ attributes = if html_builder.is_a? Hash
20
+ get_html_from_hash name, element: element, view: view
21
+ elsif html_builder.is_a? Avo::HTML::Builder
22
+ get_html_from_block name, element: element, view: view
23
+ elsif html_builder.nil?
24
+ # Handle empty html_builder by returning an empty state
25
+ default_attribute_value name
26
+ end
27
+
28
+ add_default_data_attributes attributes, name, element, view
29
+ end
30
+
31
+ private
32
+
33
+ def html_builder
34
+ return @parsed_html if @parsed_html.present?
35
+
36
+ return if @html.nil?
37
+
38
+ # Memoize the value
39
+ @parsed_html = if @html.is_a? Hash
40
+ @html
41
+ elsif @html.respond_to? :call
42
+ Avo::HTML::Builder.parse_block(record: model, resource: resource, &@html)
43
+ end
44
+
45
+ @parsed_html
46
+ end
47
+
48
+ def default_attribute_value(name)
49
+ name == :data ? {} : ""
50
+ end
51
+
52
+ def add_default_data_attributes(attributes, name, element, view)
53
+ if !attributes.nil? && name == :data && element == :input && view.in?([:edit, :new]) && resource.present?
54
+ extra_attributes = resource.get_stimulus_controllers
55
+ .split(" ")
56
+ .map do |controller|
57
+ [:"#{controller}-target", "#{id.to_s.underscore}_#{type.to_s.underscore}_input".camelize(:lower)]
58
+ end
59
+ .to_h
60
+
61
+ extra_attributes.merge attributes
62
+ else
63
+ attributes
64
+ end
65
+ end
66
+
67
+ def get_html_from_block(name = nil, element:, view:)
68
+ values = []
69
+
70
+ # get view ancestor
71
+ values << html_builder.dig_stack(view, element, name)
72
+ # get element ancestor
73
+ values << html_builder.dig_stack(element, name)
74
+ # get direct ancestor
75
+ values << html_builder.dig_stack(name)
76
+
77
+ values_type = if name == :data
78
+ :hash
79
+ else
80
+ :string
81
+ end
82
+
83
+ merge_values_as(as: values_type, values: values)
84
+ end
85
+
86
+ def get_html_from_hash(name = nil, element:, view:)
87
+ # @todo: what if this is not a Hash but a string?
88
+ html_builder.dig(view, element, name) || {}
89
+ end
90
+
91
+ # Merge the values from all possible locations.
92
+ # If the result is "blank", return nil so the attributes are not outputted to the DOM.
93
+ #
94
+ # Ex: if the style attribute is empty return `nil` instead of an empty space `" "`
95
+ def merge_values_as(as: :array, values: [])
96
+ result = if as == :array
97
+ values.flatten
98
+ elsif as == :string
99
+ values.select do |value|
100
+ value.is_a? String
101
+ end.join " "
102
+ elsif as == :hash
103
+ values.reduce({}, :merge)
104
+ end
105
+
106
+ result if result.present?
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,11 @@
1
+ module Avo
2
+ module Concerns
3
+ module HasModel
4
+ extend ActiveSupport::Concern
5
+
6
+ def has_model_id?
7
+ model.present? && model.id.present?
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ module Avo
2
+ module Concerns
3
+ module HasStimulusControllers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :stimulus_controllers, default: ""
8
+ end
9
+
10
+ def get_stimulus_controllers
11
+ return "" if view.nil?
12
+
13
+ controllers = []
14
+
15
+ case view.to_sym
16
+ when :show
17
+ controllers << "resource-show"
18
+ when :new, :edit
19
+ controllers << "resource-edit"
20
+ when :index
21
+ controllers << "resource-index"
22
+ end
23
+
24
+ controllers << self.class.stimulus_controllers
25
+
26
+ controllers.join " "
27
+ end
28
+
29
+ def stimulus_data_attributes
30
+ attributes = {
31
+ controller: get_stimulus_controllers,
32
+ }
33
+
34
+ get_stimulus_controllers.split(" ").each do |controller|
35
+ attributes["#{controller}-view-value"] = view
36
+ end
37
+
38
+ attributes
39
+ end
40
+ end
41
+ end
42
+ end
@@ -14,7 +14,7 @@ module Avo
14
14
  # resource.model_class.present?
15
15
  # end
16
16
  .map do |resource|
17
- router.resources resource.new.model_key
17
+ router.resources resource.new.route_key
18
18
  end
19
19
  end
20
20
  end
data/lib/avo/engine.rb CHANGED
@@ -17,8 +17,6 @@ module Avo
17
17
  ::Avo::App.boot
18
18
  end
19
19
 
20
- config.i18n.load_path += Dir[Avo::Engine.root.join("lib", "generators", "avo", "templates", "locales", "*.{rb,yml}")]
21
-
22
20
  initializer "avo.autoload" do |app|
23
21
  [
24
22
  ["app", "avo", "fields"],
@@ -73,7 +71,7 @@ module Avo
73
71
  begin
74
72
  Licensing::HQ.new.clear_response
75
73
  rescue => exception
76
- puts "Failed to clear Avo HQ response: #{e.message}"
74
+ puts "Failed to clear Avo HQ response: #{exception.message}"
77
75
  end
78
76
  end
79
77
  end
@@ -5,10 +5,14 @@ module Avo
5
5
  extend Avo::Fields::FieldExtensions::HasFieldName
6
6
 
7
7
  include Avo::Fields::FieldExtensions::VisibleInDifferentViews
8
- include Avo::Concerns::HandlesFieldArgs
9
8
  include ActionView::Helpers::UrlHelper
10
9
 
11
- delegate :view_context, to: "Avo::App"
10
+ include Avo::Concerns::HandlesFieldArgs
11
+ include Avo::Concerns::HasHTMLAttributes
12
+ include Avo::Fields::Concerns::IsRequired
13
+
14
+ delegate :view_context, to: ::Avo::App
15
+ delegate :simple_format, :content_tag, to: :view_context
12
16
  delegate :main_app, to: :view_context
13
17
  delegate :avo, to: :view_context
14
18
  delegate :t, to: ::I18n
@@ -51,7 +55,6 @@ module Avo
51
55
  @id = id
52
56
  @name = args[:name]
53
57
  @translation_key = args[:translation_key]
54
- @translation_enabled = ::Avo::App.translation_enabled
55
58
  @block = block
56
59
  @required = args[:required] || false
57
60
  @readonly = args[:readonly] || false
@@ -67,6 +70,7 @@ module Avo
67
70
  @as_avatar = args[:as_avatar] || false
68
71
  @as_description = args[:as_description] || false
69
72
  @index_text_align = args[:index_text_align] || :left
73
+ @html = args[:html] || nil
70
74
 
71
75
  @updatable = true
72
76
  @computable = true
@@ -80,22 +84,21 @@ module Avo
80
84
  except_on args[:except_on] if args[:except_on].present?
81
85
  end
82
86
 
83
- def hydrate(model: nil, resource: nil, action: nil, view: nil, panel_name: nil, user: nil, translation_enabled: nil)
87
+ def hydrate(model: nil, resource: nil, action: nil, view: nil, panel_name: nil, user: nil)
84
88
  @model = model if model.present?
85
89
  @view = view if view.present?
86
90
  @resource = resource if resource.present?
87
91
  @action = action if action.present?
88
92
  @user = user if user.present?
89
93
  @panel_name = panel_name if panel_name.present?
90
- @translation_enabled = translation_enabled if translation_enabled.present?
91
94
 
92
95
  self
93
96
  end
94
97
 
95
98
  def translation_key
96
- return "avo.field_translations.#{@id}" if @translation_enabled
99
+ return @translation_key if @translation_key.present?
97
100
 
98
- @translation_key
101
+ "avo.field_translations.#{@id}"
99
102
  end
100
103
 
101
104
  # Getting the name of the resource (user/users, post/posts)
@@ -105,19 +108,27 @@ module Avo
105
108
  def name
106
109
  default = @id.to_s.humanize(keep_id_suffix: true)
107
110
 
108
- return @name if @name.present?
111
+ return @name if custom_name?
109
112
 
110
- return t(translation_key, count: 1, default: default).capitalize if translation_key
111
-
112
- default
113
+ if translation_key && ::Avo::App.translation_enabled
114
+ t(translation_key, count: 1, default: default).capitalize
115
+ else
116
+ default
117
+ end
113
118
  end
114
119
 
115
120
  def plural_name
116
121
  default = name.pluralize
117
122
 
118
- return t(translation_key, count: 2, default: default).capitalize if translation_key
123
+ if translation_key && ::Avo::App.translation_enabled
124
+ t(translation_key, count: 2, default: default).capitalize
125
+ else
126
+ default
127
+ end
128
+ end
119
129
 
120
- default
130
+ def custom_name?
131
+ @name.present?
121
132
  end
122
133
 
123
134
  def placeholder
@@ -0,0 +1,17 @@
1
+ module Avo
2
+ module Fields
3
+ module Concerns
4
+ module IsRequired
5
+ extend ActiveSupport::Concern
6
+
7
+ def is_required?
8
+ if required.respond_to? :call
9
+ Avo::Hosts::ViewRecordHost.new(block: required, record: model, view: view).handle
10
+ else
11
+ required
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -10,7 +10,7 @@ module Avo
10
10
 
11
11
  super(id, **args, &block)
12
12
 
13
- @options = args[:options].present? ? args[:options] : args[:enum]
13
+ @options = args[:options] || args[:enum]
14
14
  @options = ActiveSupport::HashWithIndifferentAccess.new @options if @options.is_a? Hash
15
15
  @enum = args[:enum].present? ? args[:enum] : nil
16
16
  @display_value = args[:display_value].present? ? args[:display_value] : false
@@ -1,6 +1,6 @@
1
1
  module Avo
2
2
  class GridCollector
3
- include FieldsCollector
3
+ include Avo::Concerns::HasFields
4
4
 
5
5
  attr_accessor :cover_field
6
6
  attr_accessor :title_field
@@ -13,15 +13,15 @@ module Avo
13
13
  end
14
14
 
15
15
  def cover(field_name, as:, **args, &block)
16
- self.cover_field = parse_field(field_name, as: as, **args, &block)
16
+ self.cover_field = self.class.parse_field(field_name, as: as, **args, &block)
17
17
  end
18
18
 
19
19
  def title(field_name, as:, **args, &block)
20
- self.title_field = parse_field(field_name, as: as, **args, &block)
20
+ self.title_field = self.class.parse_field(field_name, as: as, **args, &block)
21
21
  end
22
22
 
23
23
  def body(field_name, as:, **args, &block)
24
- self.body_field = parse_field(field_name, as: as, **args, &block)
24
+ self.body_field = self.class.parse_field(field_name, as: as, **args, &block)
25
25
  end
26
26
 
27
27
  def hydrate(model:, view:, resource:)
@@ -0,0 +1,7 @@
1
+ module Avo
2
+ module Hosts
3
+ class ViewRecordHost < RecordHost
4
+ option :view
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,117 @@
1
+ class Avo::HTML::Builder
2
+ class << self
3
+ def parse_block(record: nil, resource: nil, &block)
4
+ Docile.dsl_eval(Avo::HTML::Builder.new(record: record, resource: resource), &block).build
5
+ end
6
+ end
7
+
8
+ attr_accessor :wrapper_stack
9
+ attr_accessor :data_stack
10
+ attr_accessor :style_stack
11
+ attr_accessor :classes_stack
12
+ attr_accessor :show_stack
13
+ attr_accessor :edit_stack
14
+ attr_accessor :index_stack
15
+ attr_accessor :input_stack
16
+
17
+ attr_accessor :record
18
+ attr_accessor :resource
19
+
20
+ delegate :root_path, to: Avo::App
21
+ delegate :params, to: Avo::App
22
+ delegate :current_user, to: Avo::App
23
+
24
+ def initialize(record: nil, resource: nil)
25
+ @wrapper_stack = {}
26
+ @data_stack = {}
27
+ @style_stack = ""
28
+ @classes_stack = ""
29
+ @show_stack = {}
30
+ @edit_stack = {}
31
+ @index_stack = {}
32
+ @input_stack = {}
33
+
34
+ @record = record
35
+ @resource = resource
36
+ end
37
+
38
+ def get_stack(name = nil)
39
+ # We don't have an edit component for new so we should use edit
40
+ name = :edit if name == :new
41
+
42
+ send "#{name}_stack"
43
+ end
44
+
45
+ def dig_stack(*names)
46
+ value = get_stack names.shift
47
+
48
+ if value.is_a? self.class
49
+ value.dig_stack(*names)
50
+ else
51
+ value
52
+ end
53
+ end
54
+
55
+ # payload or block
56
+ def data(payload = nil, &block)
57
+ assign_property :data, payload, &block
58
+ end
59
+
60
+ # payload or block
61
+ def style(payload = nil, &block)
62
+ assign_property :style, payload, &block
63
+ end
64
+
65
+ # payload or block
66
+ def classes(payload = nil, &block)
67
+ assign_property :classes, payload, &block
68
+ end
69
+
70
+ # Takes a block
71
+ def wrapper(&block)
72
+ capture_block :wrapper, &block
73
+ end
74
+
75
+ # Takes a block
76
+ def input(&block)
77
+ capture_block :input, &block
78
+ end
79
+
80
+ # Takes a block
81
+ def show(&block)
82
+ capture_block :show, &block
83
+ end
84
+
85
+ # Takes a block
86
+ def edit(&block)
87
+ capture_block :edit, &block
88
+ end
89
+
90
+ # Takes a block
91
+ def index(&block)
92
+ capture_block :index, &block
93
+ end
94
+
95
+ # Fetch the menu
96
+ def build
97
+ self
98
+ end
99
+
100
+ protected
101
+
102
+ # Capture and parse the blocks for the nested structure
103
+ def capture_block(property = nil, &block)
104
+ send("#{property}_stack=", self.class.parse_block(record: record, resource: resource, &block).build)
105
+ end
106
+
107
+ # Parse the properties and assign them to the blocks
108
+ def assign_property(property = :data, payload = nil, &block)
109
+ value = if block.present?
110
+ Avo::Hosts::RecordHost.new(block: block, record: record).handle
111
+ else
112
+ payload
113
+ end
114
+
115
+ send("#{property}_stack=", value)
116
+ end
117
+ end
@@ -14,6 +14,7 @@ module Avo
14
14
  :resource_ordering,
15
15
  :dashboards,
16
16
  :menu_editor,
17
+ :stimulus_js_integration,
17
18
  :advanced_fields
18
19
  ]
19
20
  end
@@ -17,4 +17,8 @@ class Avo::Menu::BaseItem
17
17
  Avo::Hosts::BaseHost.new(block: visible).handle
18
18
  end
19
19
  end
20
+
21
+ def navigation_label
22
+ label || entity_label
23
+ end
20
24
  end
@@ -2,6 +2,7 @@ class Avo::Menu::Dashboard < Avo::Menu::BaseItem
2
2
  extend Dry::Initializer
3
3
 
4
4
  option :dashboard
5
+ option :label, optional: true
5
6
 
6
7
  def parsed_dashboard
7
8
  dashboard_by_id || dashboard_by_name
@@ -14,4 +15,8 @@ class Avo::Menu::Dashboard < Avo::Menu::BaseItem
14
15
  def dashboard_by_id
15
16
  Avo::App.get_dashboard_by_id dashboard.to_s
16
17
  end
18
+
19
+ def entity_label
20
+ parsed_dashboard.navigation_label
21
+ end
17
22
  end
@@ -2,8 +2,13 @@ class Avo::Menu::Resource < Avo::Menu::BaseItem
2
2
  extend Dry::Initializer
3
3
 
4
4
  option :resource
5
+ option :label, optional: true
5
6
 
6
7
  def parsed_resource
7
8
  Avo::App.guess_resource resource.to_s
8
9
  end
10
+
11
+ def entity_label
12
+ parsed_resource.navigation_label
13
+ end
9
14
  end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.7.0" unless const_defined?(:VERSION)
2
+ VERSION = "2.9.0" unless const_defined?(:VERSION)
3
3
  end