avo 2.9.1.pre5 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -5
  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/edit_component.rb +5 -0
  11. data/app/components/avo/fields/show_component.rb +1 -1
  12. data/app/components/avo/index/ordering/button_component.rb +2 -12
  13. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  14. data/app/components/avo/index/resource_controls_component.rb +5 -1
  15. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  16. data/app/components/avo/index/table_row_component.html.erb +1 -1
  17. data/app/components/avo/item_switcher_component.html.erb +19 -0
  18. data/app/components/avo/item_switcher_component.rb +45 -0
  19. data/app/components/avo/panel_component.html.erb +23 -24
  20. data/app/components/avo/panel_component.rb +8 -5
  21. data/app/components/avo/tab_group_component.html.erb +53 -0
  22. data/app/components/avo/tab_group_component.rb +51 -0
  23. data/app/components/avo/tab_switcher_component.html.erb +21 -0
  24. data/app/components/avo/tab_switcher_component.rb +86 -0
  25. data/app/components/avo/views/resource_edit_component.html.erb +34 -56
  26. data/app/components/avo/views/resource_edit_component.rb +10 -0
  27. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  28. data/app/components/avo/views/resource_index_component.rb +3 -3
  29. data/app/components/avo/views/resource_show_component.html.erb +58 -89
  30. data/app/components/avo/views/resource_show_component.rb +2 -2
  31. data/app/controllers/avo/actions_controller.rb +1 -1
  32. data/app/controllers/avo/application_controller.rb +20 -3
  33. data/app/helpers/avo/application_helper.rb +0 -6
  34. data/app/helpers/avo/url_helpers.rb +1 -1
  35. data/app/javascript/avo.js +5 -1
  36. data/app/javascript/js/controllers/loading_button_controller.js +25 -21
  37. data/app/javascript/js/controllers/tabs_controller.js +86 -0
  38. data/app/javascript/js/controllers.js +2 -0
  39. data/app/views/avo/base/index.html.erb +1 -1
  40. data/app/views/avo/base/show.html.erb +1 -1
  41. data/app/views/avo/cards/show.html.erb +1 -1
  42. data/app/views/avo/debug/index.html.erb +1 -1
  43. data/app/views/avo/home/_actions.html.erb +1 -1
  44. data/app/views/avo/home/_dashboards.html.erb +19 -0
  45. data/app/views/avo/home/_filters.html.erb +1 -1
  46. data/app/views/avo/home/_resources.html.erb +1 -1
  47. data/app/views/avo/home/failed_to_load.html.erb +1 -1
  48. data/app/views/avo/home/index.html.erb +14 -2
  49. data/app/views/avo/partials/_javascript.html.erb +1 -1
  50. data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
  51. data/app/views/avo/private/design.html.erb +1 -1
  52. data/config/routes.rb +1 -1
  53. data/lib/avo/app.rb +9 -2
  54. data/lib/avo/base_action.rb +2 -19
  55. data/lib/avo/base_card.rb +1 -7
  56. data/lib/avo/base_resource.rb +0 -94
  57. data/lib/avo/base_resource_tool.rb +3 -1
  58. data/lib/avo/concerns/has_fields.rb +247 -50
  59. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  60. data/lib/avo/concerns/is_resource_item.rb +36 -0
  61. data/lib/avo/dashboards/base_dashboard.rb +1 -1
  62. data/lib/avo/dsl/field_parser.rb +83 -0
  63. data/lib/avo/fields/base_field.rb +19 -2
  64. data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
  65. data/lib/avo/fields/has_base_field.rb +20 -1
  66. data/lib/avo/fields/has_one_field.rb +4 -1
  67. data/lib/avo/grid_collector.rb +6 -3
  68. data/lib/avo/items_holder.rb +68 -0
  69. data/lib/avo/licensing/h_q.rb +10 -0
  70. data/lib/avo/main_panel.rb +3 -0
  71. data/lib/avo/menu/builder.rb +7 -7
  72. data/lib/avo/panel.rb +25 -0
  73. data/lib/avo/panel_builder.rb +23 -0
  74. data/lib/avo/services/uri_service.rb +71 -0
  75. data/lib/avo/tab.rb +78 -0
  76. data/lib/avo/tab_builder.rb +25 -0
  77. data/lib/avo/tab_group.rb +40 -0
  78. data/lib/avo/tab_group_builder.rb +43 -0
  79. data/lib/avo/version.rb +1 -1
  80. data/lib/avo.rb +1 -0
  81. data/lib/generators/avo/templates/resource/controller.tt +2 -0
  82. data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
  83. data/lib/generators/avo/templates/tool/view.tt +1 -1
  84. data/public/avo-assets/avo.css +27 -3
  85. data/public/avo-assets/avo.js +73 -73
  86. data/public/avo-assets/avo.js.map +3 -3
  87. metadata +24 -14
  88. data/app/assets/builds/action_cable.js +0 -2
  89. data/app/assets/builds/action_cable.js.map +0 -7
  90. data/app/assets/builds/application.js +0 -2
  91. data/app/assets/builds/application.js.map +0 -7
  92. data/app/assets/builds/avo.css +0 -9028
  93. data/app/assets/builds/avo.js +0 -512
  94. data/app/assets/builds/avo.js.map +0 -7
  95. data/app/assets/builds/avo_custom.js +0 -6
  96. data/app/assets/builds/avo_custom.js.map +0 -7
  97. data/lib/avo/concerns/has_tools.rb +0 -47
@@ -0,0 +1,19 @@
1
+ <div>
2
+ <h3>Dashboards</h3>
3
+
4
+ <div>There comes the point in your app's life when you need to display the data in an aggregated form like a metric or chart. That's what Avo's Dashboards are all about.</div>
5
+
6
+ <div>Generate a dashboard using the command below 👇👇</div>
7
+
8
+
9
+ <div class="mt-2">
10
+ <div class="mt-1">
11
+ <code class="p-1 rounded bg-sky-500 text-white">bin/rails generate avo:dashboard dashy</code>
12
+ </div>
13
+ </div>
14
+
15
+ <p>
16
+ <a href="https://docs.avohq.io/2.0/dashboards.html" target="_blank" title="Avo Dashboards documentation">Docs</a>
17
+ </p>
18
+ </div>
19
+
@@ -11,6 +11,6 @@
11
11
  </div>
12
12
  </div>
13
13
 
14
- <a href="https://docs.avohq.io/1.0/filters.html" target="_blank" title="Avo Filters documentation" class="text-bold cursor-pointer block mt-2">Filters in the docs 👉</a>
14
+ <a href="https://docs.avohq.io/2.0/filters.html" target="_blank" title="Avo Filters documentation" class="text-bold cursor-pointer block mt-2">Filters in the docs 👉</a>
15
15
  </div>
16
16
  </div>
@@ -35,7 +35,7 @@
35
35
  <% end %>
36
36
 
37
37
  <p>
38
- <a href="https://docs.avohq.io/1.0/resources.html" target="_blank" title="Avo Resources documentation">Docs</a>
38
+ <a href="https://docs.avohq.io/2.0/resources.html" target="_blank" title="Avo Resources documentation">Docs</a>
39
39
  </p>
40
40
  </div>
41
41
 
@@ -1,4 +1,4 @@
1
- <%= turbo_frame_wrap(params[:turbo_frame]) do %>
1
+ <%= render Avo::TurboFrameWrapperComponent.new(params[:turbo_frame]) do %>
2
2
  <%
3
3
  classes = 'absolute inset-auto left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2'
4
4
  label = t 'avo.failed_to_load'
@@ -1,5 +1,5 @@
1
1
  <div class="flex flex-col">
2
- <%= render Avo::PanelComponent.new title: 'Welcome to Avo', description: 'This page is visible only in development. It will be hidden in other environments.' do |c| %>
2
+ <%= render Avo::PanelComponent.new(title: 'Welcome to Avo', description: 'This page is visible only in development. It will be hidden in other environments.') do |c| %>
3
3
  <% c.body do %>
4
4
  <div class="flex flex-col justify-between py-6 min-h-24">
5
5
  <div class="px-6 space-y-4">
@@ -23,10 +23,20 @@
23
23
  <li><span class="mr-2">👍</span> Grid view</li>
24
24
  <li><span class="mr-2">👍</span> Authorization</li>
25
25
  <li><span class="mr-2">👍</span> Localization</li>
26
+ <li><span class="mr-2">👍</span> Resource tools</li>
26
27
  <li><span class="mr-2">👍</span> Custom tools</li>
27
28
  <li><span class="mr-2">👍</span> Custom fields</li>
29
+ <li><span class="mr-2">👍</span> Menu editor</li>
28
30
  <li><span class="mr-2">👍</span> Search</li>
29
- <li><span class="mr-2">🕐</span> Dashboards</li>
31
+ <li><span class="mr-2">👍</span> Dashboards</li>
32
+ <li><span class="mr-2">👍</span> Responsive design</li>
33
+ <li><span class="mr-2">👍</span> Menu editor</li>
34
+ <li><span class="mr-2">👍</span> Stimulus JS integration</li>
35
+ <li><span class="mr-2">👍</span> Tabs and panels</li>
36
+ <li><span class="mr-2">🕐</span> Resource cards</li>
37
+ <li><span class="mr-2">🕐</span> Track resource changes</li>
38
+ <li><span class="mr-2">🕐</span> Smart resource generation</li>
39
+ <li><span class="mr-2">🕐</span> Records preview</li>
30
40
  <li><span class="mr-2">🕐</span> Themes</li>
31
41
  </ul>
32
42
  </p>
@@ -37,6 +47,8 @@
37
47
  <div class="space">
38
48
  <%= render partial: 'resources' %>
39
49
  <hr class="my-6">
50
+ <%= render partial: 'dashboards' %>
51
+ <hr class="my-6">
40
52
  <%= render partial: 'filters' %>
41
53
  <hr class="my-6">
42
54
  <%= render partial: 'actions' %>
@@ -1,6 +1,6 @@
1
1
  <%= javascript_tag nonce: true do %>
2
2
  window.Avo = window.Avo || { configuration: {} }
3
3
  Avo.configuration.timezone = '<%= Avo.configuration.timezone %>'
4
- Avo.configuration.root_path = '<%= Avo::App.root_path %>'
4
+ Avo.configuration.root_path = '<%= Avo.configuration.root_path %>'
5
5
  Avo.configuration.search_debounce = '<%= Avo.configuration.search_debounce %>'
6
6
  <% end %>
@@ -0,0 +1,20 @@
1
+ <div class="flex">
2
+ <div class="button-group">
3
+ <% tabs.each do |tab| %>
4
+ <% is_active_view = tab.name.to_s == active_tab_name.to_s %>
5
+ <%= a_link resource_path(resource: @resource, model: @resource.model, keep_query_params: true, active_tab_name: tab.name, tab_turbo_frame: group.turbo_frame_id),
6
+ color: is_active_view ? :gray : :primary,
7
+ rounded: false,
8
+ size: :sm,
9
+ class: is_active_view ? ' bg-gray-100 border-gray-300' : ' z-20',
10
+ title: tab.description || tab.name,
11
+ data: {
12
+ tippy: 'tooltip',
13
+ 'turbo-frame': group.turbo_frame_id,
14
+ control: "view-type-toggle-#{tab}"
15
+ } do %>
16
+ <%= tab.name %>
17
+ <% end %>
18
+ <% end %>
19
+ </div>
20
+ </div>
@@ -1,5 +1,5 @@
1
1
  <div class="flex flex-col">
2
- <%= render Avo::PanelComponent.new title: 'Welcome to Avo', description: 'This page is visible only in development. It will be hidden in other environments.' do |c| %>
2
+ <%= render Avo::PanelComponent.new(title: 'Welcome to Avo', description: 'This page is visible only in development. It will be hidden in other environments.') do |c| %>
3
3
  <% c.tools do %>
4
4
  <%= a_link('/admin', icon: 'arrow-left', style: :primary, is_link: true) do %>
5
5
  Primary
data/config/routes.rb CHANGED
@@ -23,7 +23,7 @@ Avo::Engine.routes.draw do
23
23
  delete "/:resource_name/:id/active_storage_attachments/:attachment_name/:attachment_id", to: "attachments#destroy"
24
24
 
25
25
  # Ordering
26
- patch "/:resource_name/:id/order", to: "resources#order"
26
+ patch "/:resource_name/:id/order", to: "resources#order", as: "order"
27
27
  patch "/:resource_name/:id/:related_name/:related_id/order", to: "associations#order", as: "associations_order"
28
28
 
29
29
  # Actions
data/lib/avo/app.rb CHANGED
@@ -29,13 +29,20 @@ module Avo
29
29
  end
30
30
  end
31
31
 
32
- def init(request:, context:, current_user:, root_path:, view_context:, params:)
32
+ # Renerate a dynamic root path using the URIService
33
+ def root_path(paths: [], query: {}, **args)
34
+ Avo::Services::URIService.parse(view_context.avo.root_url.to_s)
35
+ .append_paths(paths)
36
+ .append_query(query)
37
+ .to_s
38
+ end
39
+
40
+ def init(request:, context:, current_user:, view_context:, params:)
33
41
  self.error_messages = []
34
42
  self.context = context
35
43
  self.current_user = current_user
36
44
  self.params = params
37
45
  self.request = request
38
- self.root_path = root_path
39
46
  self.view_context = view_context
40
47
 
41
48
  self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
@@ -13,7 +13,6 @@ module Avo
13
13
  class_attribute :view
14
14
  class_attribute :user
15
15
  class_attribute :resource
16
- class_attribute :invalid_fields
17
16
  class_attribute :standalone, default: false
18
17
  class_attribute :visible
19
18
  class_attribute :may_download_file, default: false
@@ -22,7 +21,8 @@ module Avo
22
21
  attr_accessor :model
23
22
  attr_accessor :resource
24
23
  attr_accessor :user
25
- attr_accessor :fields_loader
24
+
25
+ delegate :view, to: :class
26
26
 
27
27
  class << self
28
28
  def form_data_attributes
@@ -68,23 +68,6 @@ module Avo
68
68
  self.class.context
69
69
  end
70
70
 
71
- def get_field_definitions
72
- return [] if self.class.fields.blank?
73
-
74
- self.class.fields.map do |field|
75
- field.hydrate(action: self)
76
- end
77
- end
78
-
79
- def get_fields
80
- get_field_definitions.map do |field|
81
- field.hydrate(action: self, model: @model)
82
- end
83
- .select do |field|
84
- field.visible?
85
- end
86
- end
87
-
88
71
  def get_attributes_for_action
89
72
  get_fields.map do |field|
90
73
  [field.id, field.value]
data/lib/avo/base_card.rb CHANGED
@@ -59,13 +59,7 @@ module Avo
59
59
  def frame_url(enforced_range: nil, params: {})
60
60
  enforced_range ||= initial_range || ranges.first
61
61
 
62
- # append the parent params to the card request
63
- begin
64
- other_params = "&#{params.permit!.to_h.map { |k, v| "#{k}=#{v}" }.join("&")}"
65
- rescue
66
- end
67
-
68
- "#{Avo::App.root_path}/dashboards/#{dashboard.id}/cards/#{id}?turbo_frame=#{turbo_frame}&index=#{index}&range=#{enforced_range}#{other_params}"
62
+ Avo::App.view_context.avo.dashboard_card_path(dashboard.id, id, turbo_frame: turbo_frame, index: index, range: enforced_range, **params.permit!)
69
63
  end
70
64
 
71
65
  def card_classes
@@ -4,7 +4,6 @@ module Avo
4
4
  extend HasContext
5
5
 
6
6
  include ActionView::Helpers::UrlHelper
7
- include Avo::Concerns::HasTools
8
7
  include Avo::Concerns::HasModel
9
8
  include Avo::Concerns::HasFields
10
9
  include Avo::Concerns::HasStimulusControllers
@@ -44,7 +43,6 @@ module Avo
44
43
  class_attribute :hide_from_global_search, default: false
45
44
  class_attribute :after_create_path, default: :show
46
45
  class_attribute :after_update_path, default: :show
47
- class_attribute :invalid_fields
48
46
  class_attribute :record_selector, default: true
49
47
  class_attribute :keep_filters_panel_open, default: false
50
48
 
@@ -123,80 +121,6 @@ module Avo
123
121
  self
124
122
  end
125
123
 
126
- def get_field_definitions
127
- return [] if self.class.fields.blank?
128
-
129
- fields = self.class.fields.map do |field|
130
- field.hydrate(resource: self, panel_name: default_panel_name, user: user)
131
- end
132
-
133
- if Avo::App.license.lacks_with_trial(:custom_fields)
134
- fields = fields.reject do |field|
135
- field.custom?
136
- end
137
- end
138
-
139
- if Avo::App.license.lacks_with_trial(:advanced_fields)
140
- fields = fields.reject do |field|
141
- field.type == "tags"
142
- end
143
- end
144
-
145
- fields
146
- end
147
-
148
- def get_fields(panel: nil, reflection: nil)
149
- fields = get_field_definitions
150
- .select do |field|
151
- field.send("show_on_#{@view}")
152
- end
153
- .select do |field|
154
- field.visible?
155
- end
156
- .select do |field|
157
- is_valid = true
158
-
159
- # Strip out the reflection field in index queries with a parent association.
160
- if reflection.present?
161
- # regular non-polymorphic association
162
- # we're matching the reflection inverse_of foriegn key with the field's foreign_key
163
- if field.is_a?(Avo::Fields::BelongsToField)
164
- if field.respond_to?(:foreign_key) &&
165
- reflection.inverse_of.present? &&
166
- reflection.inverse_of.foreign_key == field.foreign_key
167
- is_valid = false
168
- end
169
-
170
- # polymorphic association
171
- if field.respond_to?(:foreign_key) &&
172
- field.is_polymorphic? &&
173
- reflection.respond_to?(:polymorphic?) &&
174
- reflection.inverse_of.foreign_key == field.reflection.foreign_key
175
- is_valid = false
176
- end
177
- end
178
- end
179
-
180
- is_valid
181
- end
182
-
183
- if panel.present?
184
- fields = fields.select do |field|
185
- field.panel_name == panel
186
- end
187
- end
188
-
189
- hydrate_fields(model: @model, view: @view)
190
-
191
- fields
192
- end
193
-
194
- def get_field(id)
195
- get_field_definitions.find do |f|
196
- f.id == id.to_sym
197
- end
198
- end
199
-
200
124
  def get_grid_fields
201
125
  return if self.class.grid_loader.blank?
202
126
 
@@ -215,14 +139,6 @@ module Avo
215
139
  self.class.actions_loader.bag
216
140
  end
217
141
 
218
- def hydrate_fields(model: nil, view: nil)
219
- fields.map do |field|
220
- field.hydrate(model: @model, view: @view, resource: self)
221
- end
222
-
223
- self
224
- end
225
-
226
142
  def default_panel_name
227
143
  return @params[:related_name].capitalize if @params.present? && @params[:related_name].present?
228
144
 
@@ -236,16 +152,6 @@ module Avo
236
152
  end
237
153
  end
238
154
 
239
- def panels
240
- [
241
- {
242
- name: default_panel_name,
243
- type: :fields,
244
- in_panel: true
245
- }
246
- ]
247
- end
248
-
249
155
  def class_name_without_resource
250
156
  self.class.name.demodulize.chomp("Resource")
251
157
  end
@@ -1,9 +1,11 @@
1
1
  module Avo
2
2
  class BaseResourceTool
3
+ include Avo::Concerns::IsResourceItem
3
4
  include Avo::Fields::FieldExtensions::VisibleInDifferentViews
4
5
 
5
6
  class_attribute :name
6
7
  class_attribute :partial
8
+ class_attribute :item_type, default: :tool
7
9
 
8
10
  attr_accessor :params
9
11
  attr_accessor :resource
@@ -20,7 +22,7 @@ module Avo
20
22
  end
21
23
 
22
24
  def hydrate(view: nil)
23
- @view = view
25
+ @view = view if view.present?
24
26
 
25
27
  self
26
28
  end