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
@@ -1,3 +1,3 @@
1
1
  <div <% if @index == 0 %> class="overflow-hidden rounded-t" <% end %> data-field-id="<%= @field.id %>">
2
- <%= render Avo::Fields::Common::HeadingComponent.new value: @field.name, as_html: @field.as_html %>
2
+ <%= render Avo::Fields::Common::HeadingComponent.new value: @field.name, as_html: @field.as_html, empty: @field.empty %>
3
3
  </div>
@@ -1,3 +1,3 @@
1
1
  <div <% if @index == 0 %> class="overflow-hidden rounded-t" <% end %> data-field-id="<%= @field.id %>">
2
- <%= render Avo::Fields::Common::HeadingComponent.new value: @field.name, as_html: @field.as_html %>
2
+ <%= render Avo::Fields::Common::HeadingComponent.new value: @field.name, as_html: @field.as_html, empty: @field.empty %>
3
3
  </div>
@@ -1,3 +1,3 @@
1
- <%= index_field_wrapper field: @field, resource: @resource, class: 'whitespace-no-wrap w-[1%]' do %>
1
+ <%= index_field_wrapper **field_wrapper_args, class: 'whitespace-no-wrap w-[1%]' do %>
2
2
  <% link_to_if (@field.link_to_resource or Avo.configuration.id_links_to_resource), @field.value, resource_view_path, title: t('avo.view_item', item: @resource.name).humanize %>
3
3
  <% end %>
@@ -3,13 +3,15 @@
3
3
  class Avo::Fields::IndexComponent < ViewComponent::Base
4
4
  include Avo::ResourcesHelper
5
5
 
6
+ attr_reader :parent_resource
6
7
  attr_reader :view
7
8
 
8
- def initialize(field: nil, resource: nil, index: 0, parent_model: nil)
9
+ def initialize(field: nil, resource: nil, index: 0, parent_model: nil, parent_resource: nil)
9
10
  @field = field
10
11
  @resource = resource
11
12
  @index = index
12
13
  @parent_model = parent_model
14
+ @parent_resource = parent_resource
13
15
  @view = :index
14
16
  end
15
17
 
@@ -18,11 +20,18 @@ class Avo::Fields::IndexComponent < ViewComponent::Base
18
20
 
19
21
  if @parent_model.present?
20
22
  args = {
21
- via_resource_class: @parent_model.class,
23
+ via_resource_class: @parent_resource.class,
22
24
  via_resource_id: @parent_model.id
23
25
  }
24
26
  end
25
27
 
26
28
  helpers.resource_view_path(model: @resource.model, resource: @resource, **args)
27
29
  end
30
+
31
+ def field_wrapper_args
32
+ {
33
+ field: @field,
34
+ resource: @resource
35
+ }
36
+ end
28
37
  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 %>
@@ -1,3 +1,3 @@
1
- <%= index_field_wrapper field: @field, resource: @resource, flush: true do %>
1
+ <%= index_field_wrapper **field_wrapper_args, flush: true do %>
2
2
  <%= render Avo::Fields::Common::ProgressBarComponent.new value: @field.value, display_value: @field.display_value, value_suffix: @field.value_suffix, max: @field.max, view: view %>
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.label %>
3
3
  <% end %>
@@ -3,15 +3,19 @@
3
3
  class Avo::Fields::ShowComponent < ViewComponent::Base
4
4
  include Avo::ResourcesHelper
5
5
 
6
+ attr_reader :compact
6
7
  attr_reader :field
7
8
  attr_reader :index
8
9
  attr_reader :resource
10
+ attr_reader :stacked
9
11
  attr_reader :view
10
12
 
11
- def initialize(field: nil, resource: nil, index: 0, form: nil)
13
+ def initialize(field: nil, resource: nil, index: 0, form: nil, compact: false, stacked: false)
14
+ @compact = compact
12
15
  @field = field
13
16
  @index = index
14
17
  @resource = resource
18
+ @stacked = stacked
15
19
  @view = :show
16
20
  end
17
21
 
@@ -40,9 +44,11 @@ class Avo::Fields::ShowComponent < ViewComponent::Base
40
44
 
41
45
  def field_wrapper_args
42
46
  {
47
+ compact: compact,
43
48
  field: field,
44
49
  index: index,
45
50
  resource: resource,
51
+ stacked: stacked,
46
52
  view: view
47
53
  }
48
54
  end
@@ -1,3 +1,3 @@
1
- <%= index_field_wrapper field: @field, resource: @resource do %>
1
+ <%= index_field_wrapper **field_wrapper_args do %>
2
2
  <%= render Avo::Fields::Common::StatusViewerComponent.new label: @field.value, status: @field.status %>
3
3
  <% end %>
@@ -1,4 +1,4 @@
1
- <%= index_field_wrapper field: @field, resource: @resource, flush: true do %>
1
+ <%= index_field_wrapper **field_wrapper_args, flush: true do %>
2
2
  <div class="flex gap-1 items-center flex-nowrap">
3
3
  <% value.take(3).each do |item| %>
4
4
  <%= render Avo::Fields::TagsField::TagComponent.new(label: label_from_item(item)) %>
@@ -4,6 +4,8 @@
4
4
  data: @field.get_html(:data, view: view, element: :input),
5
5
  disabled: @field.is_readonly?,
6
6
  placeholder: @field.placeholder,
7
- style: @field.get_html(:style, view: view, element: :input)
7
+ style: @field.get_html(:style, view: view, element: :input),
8
+ # value: @field.value,
9
+ multiple: multiple
8
10
  %>
9
11
  <% end %>
@@ -1,4 +1,4 @@
1
- <%= index_field_wrapper field: @field, resource: @resource do %>
1
+ <%= index_field_wrapper **field_wrapper_args do %>
2
2
  <% if @field.as_html %>
3
3
  <%== @field.value %>
4
4
  <% elsif @field.protocol.present? %>
@@ -1,4 +1,4 @@
1
- <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, full_width: true do %>
1
+ <%= field_wrapper **field_wrapper_args, full_width: true do %>
2
2
  <%= content_tag :div, class: "relative block overflow-x-auto max-w-full",
3
3
  data: {
4
4
  controller: "trix-field",
@@ -1,5 +1,5 @@
1
1
  <div data-controller="toggle-panel">
2
- <div class="relative w-full flex justify-between z-30">
2
+ <div class="relative w-full flex justify-between">
3
3
  <%= a_button class: 'focus:outline-none',
4
4
  color: :primary,
5
5
  size: :sm,
@@ -14,7 +14,7 @@
14
14
  <% end %>
15
15
  <% end %>
16
16
  <div
17
- class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-20 shadow-modal rounded divide-y divide-gray-300 <%= 'hidden' unless params[:keep_filters_panel_open] == '1' %>"
17
+ class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-40 shadow-modal rounded divide-y divide-gray-300 <%= 'hidden' unless params[:keep_filters_panel_open] == '1' %>"
18
18
  data-toggle-panel-target="panel"
19
19
  >
20
20
  <% @filters.each do |filter| %>
@@ -1,3 +1,3 @@
1
1
  <div class="absolute bg-gray-50 w-full h-full">
2
- <%= helpers.svg 'avocado', class: 'relative transform -translate-x-1/2 -translate-y-1/2 h-20 text-gray-400 inset-auto top-1/2 left-1/2' %>
2
+ <%= image_tag Avo.configuration.branding.placeholder, class: 'relative transform -translate-x-1/2 -translate-y-1/2 h-20 text-gray-400 inset-auto top-1/2 left-1/2' %>
3
3
  </div>
@@ -2,7 +2,7 @@
2
2
  class="relative bg-white rounded shadow-modal flex flex-col group"
3
3
  <%== item_selector_init @resource %>
4
4
  >
5
- <div class="relative w-full pb-3/4 rounded-t overflow-hidden">
5
+ <%= content_tag :div, class: "relative w-full pb-3/4 rounded-t overflow-hidden #{cover.present? ? cover.get_html(:classes, view: :index, element: :wrapper) : ""}", style: cover.present? ? cover.get_html(:style, view: :index, element: :wrapper) : "" do %>
6
6
  <% if @resource.record_selector %>
7
7
  <%== item_selector_input floating: true, size: :lg %>
8
8
  <% end %>
@@ -17,9 +17,7 @@
17
17
  <%= link_to_if cover.link_to_resource, image_tag(helpers.main_app.url_for(cover.value.variant(resize_to_limit: [480, 480])), class: 'absolute h-full w-full object-cover'), resource_view_path, class: 'absolute h-full w-full object-cover', title: title.value %>
18
18
  <% else %>
19
19
  <%= link_to resource_view_path do %>
20
- <div class="absolute bg-gray-100 w-full h-full">
21
- <%= helpers.svg 'avocado', class: 'relative transform -translate-x-1/2 -translate-y-1/2 h-20 text-gray-400 inset-auto top-1/2 left-1/2' %>
22
- </div>
20
+ <%= render Avo::Index::GridCoverEmptyStateComponent.new %>
23
21
  <% end %>
24
22
  <% end %>
25
23
  <% elsif cover.value.present? %>
@@ -27,18 +25,22 @@
27
25
  <% else %>
28
26
  <%= render Avo::Index::GridCoverEmptyStateComponent.new %>
29
27
  <% end %>
30
- </div>
31
- <div class="grid grid-cols-1 place-content-between p-4 h-full">
28
+ <% end %>
29
+ <%= content_tag :div, class: "grid grid-cols-1 place-content-between p-4 h-full" do %>
32
30
  <div class="mb-4 h-full flex flex-col space-between">
33
- <div class="grid font-semibold leading-tight text-lg mb-2">
34
- <%= link_to_if title.link_to_resource, title.value, resource_view_path if title.present? %>
35
- </div>
36
- <div class="text-sm break-words text-gray-500">
37
- <%= body.value if body.present? %>
38
- </div>
31
+ <% if title.present? %>
32
+ <%= content_tag :div, class: "grid font-semibold leading-tight text-lg mb-2 #{title.get_html(:classes, view: :index, element: :wrapper)}", style: title.get_html(:style, view: :index, element: :wrapper) do %>
33
+ <%= link_to_if title.link_to_resource, title.value, resource_view_path %>
34
+ <% end %>
35
+ <% end %>
36
+ <% if body.present? %>
37
+ <%= content_tag :div, class: "text-sm break-words text-gray-500 #{body.get_html(:classes, view: :index, element: :wrapper)}", style: body.get_html(:style, view: :index, element: :wrapper) do %>
38
+ <%= body.value %>
39
+ <% end %>
40
+ <% end %>
39
41
  </div>
40
42
  <div class="w-full place-self-end">
41
43
  <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, view_type: :grid)) %>
42
44
  </div>
43
- </div>
45
+ <% end %>
44
46
  </div>
@@ -32,7 +32,7 @@ class Avo::Index::GridItemComponent < ViewComponent::Base
32
32
 
33
33
  if @parent_model.present?
34
34
  args = {
35
- via_resource_class: parent_resource.model_class,
35
+ via_resource_class: parent_resource.class.to_s,
36
36
  via_resource_id: @parent_model.id
37
37
  }
38
38
  end
@@ -22,7 +22,7 @@
22
22
  %>
23
23
  <%= helpers.svg('switch-vertical', class: 'text-gray-400 h-6 hover:text-gray-600') %>
24
24
  <% end %>
25
- <div class="flex hidden absolute max-w-xs bg-white rounded p-2 z-40" data-popover-target="content">
25
+ <div class="flex hidden absolute max-w-xs bg-white rounded p-2" data-popover-target="content">
26
26
  <%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, reflection: @reflection, direction: :higher, svg: 'arrow-up' %>
27
27
  <%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, reflection: @reflection, direction: :lower, svg: 'arrow-down' %>
28
28
  <%= render Avo::Index::Ordering::ButtonComponent.new resource: @resource, reflection: @reflection, direction: :to_top, svg: 'download-solid-reversed' %>
@@ -36,7 +36,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
36
36
 
37
37
  if @parent_model.present?
38
38
  args = {
39
- via_resource_class: parent_resource.model_class,
39
+ via_resource_class: parent_resource.class.to_s,
40
40
  via_resource_id: @parent_model.id
41
41
  }
42
42
  end
@@ -50,7 +50,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
50
50
 
51
51
  if @parent_model.present?
52
52
  args = {
53
- via_resource_class: parent_resource.model_class,
53
+ via_resource_class: parent_resource.class.to_s,
54
54
  via_resource_id: @parent_model.id
55
55
  }
56
56
  end
@@ -17,7 +17,7 @@
17
17
  </td>
18
18
  <% end %>
19
19
  <% @resource.get_fields(reflection: @reflection, only_root: true).each_with_index do |field, index| %>
20
- <%= render field.component_for_view(:index).new(field: field, resource: @resource, index: index, parent_model: @parent_model) %>
20
+ <%= render field.component_for_view(:index).new(field: field, resource: @resource, index: index, parent_model: @parent_model, parent_resource: @parent_resource) %>
21
21
  <% end %>
22
22
  <% if Avo.configuration.resource_controls_on_the_right? %>
23
23
  <td class="text-right whitespace-nowrap px-2" data-control="resource-controls">
@@ -24,8 +24,15 @@
24
24
  </div>
25
25
  <% end %>
26
26
  <% if body? %>
27
- <div class="relative <%= white_panel_classes %> <%= @body_classes %>">
28
- <%= body %>
27
+ <div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:gap-4 w-full">
28
+ <div class="relative flex-1 overflow-auto <%= white_panel_classes %> <%= @body_classes %> <% if sidebar? %> w-2/3 <% end %>">
29
+ <%= body %>
30
+ </div>
31
+ <% if sidebar? %>
32
+ <div class="w-full sm:w-1/3 flex-shrink-0 h-full <%= white_panel_classes %>">
33
+ <%= sidebar %>
34
+ </div>
35
+ <% end %>
29
36
  </div>
30
37
  <% end %>
31
38
  <% if bare_content? %>
@@ -5,8 +5,11 @@ class Avo::PanelComponent < ViewComponent::Base
5
5
  attr_reader :name
6
6
  attr_reader :classes
7
7
 
8
+ delegate :white_panel_classes, to: :helpers
9
+
8
10
  renders_one :tools
9
11
  renders_one :body
12
+ renders_one :sidebar
10
13
  renders_one :bare_content
11
14
  renders_one :footer_tools
12
15
  renders_one :footer
@@ -25,10 +28,6 @@ class Avo::PanelComponent < ViewComponent::Base
25
28
 
26
29
  private
27
30
 
28
- def white_panel_classes
29
- "bg-white rounded shadow"
30
- end
31
-
32
31
  def data_attributes
33
32
  @data.merge({"panel-index": @index})
34
33
  end
@@ -85,6 +85,16 @@ class Avo::ResourceComponent < Avo::BaseComponent
85
85
  end
86
86
  end
87
87
 
88
+ def sidebar
89
+ return if Avo::App.license.lacks_with_trial(:resource_sidebar)
90
+
91
+ @sidebar ||= search_for_sidebar
92
+ end
93
+
94
+ def sidebar_component(form: nil)
95
+ Avo::ResourceSidebarComponent.new resource: @resource, fields: sidebar.items, params: params, view: view, form: form
96
+ end
97
+
88
98
  def has_reflection_and_is_read_only
89
99
  if @reflection.present? && @reflection.active_record.name && @reflection.name
90
100
  fields = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name).get_field_definitions
@@ -105,4 +115,12 @@ class Avo::ResourceComponent < Avo::BaseComponent
105
115
  def via_resource?
106
116
  (params[:via_resource_class].present? || params[:via_relation_class].present?) && params[:via_resource_id].present?
107
117
  end
118
+
119
+ def search_for_sidebar
120
+ item = @resource.get_items.find do |item|
121
+ item.is_sidebar?
122
+ end
123
+
124
+ item&.hydrate(view: view)
125
+ end
108
126
  end
@@ -0,0 +1,19 @@
1
+ <div class="resource-sidebar-component divide-y">
2
+ <% fields.each_with_index do |field, index| %>
3
+ <%= render field
4
+ .hydrate(
5
+ resource: resource,
6
+ model: resource.model,
7
+ user: resource.user,
8
+ view: view
9
+ ).component_for_view(view)
10
+ .new(
11
+ field: field,
12
+ resource: resource,
13
+ form: form,
14
+ stacked: true,
15
+ index: index
16
+ )
17
+ %>
18
+ <% end %>
19
+ </div>
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::ResourceSidebarComponent < ViewComponent::Base
4
+ attr_reader :resource
5
+ attr_reader :params
6
+ attr_reader :view
7
+ attr_reader :form
8
+
9
+ def initialize(resource: nil, fields: nil, index: nil, params: nil, form: nil, view: nil)
10
+ @resource = resource
11
+ @fields = fields
12
+ @params = params
13
+ @view = view
14
+ @form = form
15
+ end
16
+
17
+ def fields
18
+ @fields.filter do |field|
19
+ field.visible_on? view
20
+ end
21
+ end
22
+
23
+ def render?
24
+ Avo::App.license.has_with_trial(:resource_sidebar)
25
+ end
26
+ end
@@ -22,7 +22,7 @@
22
22
  <%= helpers.svg 'three-dots', class: 'h-4' %>
23
23
  </a>
24
24
  <div
25
- class="hidden absolute flex flex-col inset-auto right-0 -mt-12 bg-white rounded min-w-[200px] shadow-context -translate-y-full"
25
+ class="hidden absolute flex flex-col inset-auto right-0 -mt-12 bg-white rounded min-w-[200px] shadow-context -translate-y-full z-40"
26
26
  data-toggle-panel-target="panel"
27
27
  >
28
28
  <% if Avo::App.has_profile_menu? %>
@@ -20,13 +20,13 @@
20
20
  </div>
21
21
  </div>
22
22
  <% else %>
23
- <div class="flex flex-wrap gap-2" data-target="tab-switcher" data-style="pills">
23
+ <div class="flex flex-wrap gap-2 p-2 <%= white_panel_classes %>" data-target="tab-switcher" data-style="pills">
24
24
  <% visible_items.each do |tab| %>
25
25
  <%= a_link tab_path(tab),
26
26
  color: selected?(tab) ? :primary : :gray,
27
27
  style: selected?(tab) ? :outline : :text,
28
28
  size: :sm,
29
- class: selected?(tab) ? "z-20" : "",
29
+ class: selected?(tab) ? "z-20 bg-primary-100" : "",
30
30
  title: tab.description,
31
31
  data: {
32
32
  tippy: tab.description.present? ? 'tooltip' : '',
@@ -11,6 +11,8 @@ class Avo::TabSwitcherComponent < Avo::BaseComponent
11
11
  attr_reader :view
12
12
  attr_reader :style
13
13
 
14
+ delegate :white_panel_classes, to: :helpers
15
+
14
16
  def initialize(resource:, group:, current_tab:, active_tab_name:, view:, style:)
15
17
  @active_tab_name = active_tab_name
16
18
  @resource = resource
@@ -19,12 +19,12 @@
19
19
  <%= render Avo::PanelComponent.new(name: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: 0, data: { panel_id: "main" }) do |c| %>
20
20
  <% c.tools do %>
21
21
  <%= a_link back_path,
22
- style: :text,
23
- icon: 'arrow-left' do %>
24
- <%= t('avo.cancel').capitalize %>
25
- <% end %>
26
- <% if can_see_the_destroy_button? %>
27
- <%= a_link destroy_path,
22
+ style: :text,
23
+ icon: 'arrow-left' do %>
24
+ <%= t('avo.cancel').capitalize %>
25
+ <% end %>
26
+ <% if can_see_the_destroy_button? %>
27
+ <%= a_link destroy_path,
28
28
  method: :delete,
29
29
  local: true,
30
30
  style: :text,
@@ -37,36 +37,42 @@
37
37
  control: :destroy,
38
38
  'resource-id': @resource.model.id,
39
39
  } do %>
40
- <%= t('avo.delete').capitalize %>
40
+ <%= t('avo.delete').capitalize %>
41
+ <% end %>
41
42
  <% end %>
42
- <% end %>
43
- <% if can_see_the_actions_button? %>
44
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
45
- <% end %>
46
- <% if can_see_the_save_button? %>
47
- <%= a_button color: :primary,
43
+ <% if can_see_the_actions_button? %>
44
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
45
+ <% end %>
46
+ <% if can_see_the_save_button? %>
47
+ <%= a_button color: :primary,
48
48
  style: :primary,
49
49
  loading: true,
50
50
  type: :submit,
51
51
  icon: 'save' do %>
52
- <%= t('avo.save').capitalize %>
52
+ <%= t('avo.save').capitalize %>
53
+ <% end %>
53
54
  <% end %>
54
55
  <% end %>
55
- <% end %>
56
- <%# Extract the main panel and display the fields here. %>
57
- <%# This way we'll be able to render the footer buttons under the main fields. %>
58
- <% if main_panel.present? %>
59
- <% c.body do %>
60
- <div class="divide-y">
61
- <% main_panel.items.each_with_index do |field, index| %>
62
- <%= render field.hydrate(resource: @resource, model: @resource.model, user: @resource.user, view: view_for(field))
63
- .component_for_view(view_for field)
64
- .new(field: field, resource: @resource, index: index, form: form)
65
- %>
56
+ <%# Extract the main panel and display the fields here. %>
57
+ <%# This way we'll be able to render the footer buttons under the main fields. %>
58
+ <% if main_panel.present? %>
59
+ <% c.body do %>
60
+ <div class="divide-y">
61
+ <% main_panel.items.each_with_index do |field, index| %>
62
+ <%= render field
63
+ .hydrate(resource: @resource, model: @resource.model, user: @resource.user, view: view_for(field))
64
+ .component_for_view(view_for field)
65
+ .new(field: field, resource: @resource, index: index, form: form, compact: sidebar.present?)
66
+ %>
66
67
  <% end %>
67
68
  </div>
68
69
  <% end %>
69
70
  <% end %>
71
+ <% if sidebar.present? %>
72
+ <% c.sidebar do %>
73
+ <%= render sidebar_component form: form %>
74
+ <% end %>
75
+ <% end %>
70
76
  <% if Avo.configuration.buttons_on_form_footers %>
71
77
  <% c.footer_tools do %>
72
78
  <div class="mt-4">
@@ -31,7 +31,7 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
31
31
  end
32
32
 
33
33
  def resource_view_path
34
- helpers.resource_view_path(model: relation_resource.model, resource: relation_resource)
34
+ helpers.resource_view_path(model: association_resource.model, resource: association_resource)
35
35
  end
36
36
 
37
37
  def can_see_the_destroy_button?
@@ -160,14 +160,20 @@
160
160
  <% c.body do %>
161
161
  <div class="divide-y">
162
162
  <% main_panel.items.each_with_index do |field, index| %>
163
- <%= render field.hydrate(resource: @resource, model: @resource.model, user: @resource.user, view: view)
163
+ <%= render field
164
+ .hydrate(resource: @resource, model: @resource.model, user: @resource.user, view: view)
164
165
  .component_for_view(view)
165
- .new(field: field, resource: @resource, index: index)
166
+ .new(field: field, resource: @resource, index: index, compact: sidebar.present?)
166
167
  %>
167
168
  <% end %>
168
169
  </div>
169
170
  <% end %>
170
171
  <% end %>
172
+ <% if sidebar.present? %>
173
+ <% c.sidebar do %>
174
+ <%= render sidebar_component %>
175
+ <% end %>
176
+ <% end %>
171
177
  <% end %>
172
178
  <% if @reflection.blank? %>
173
179
  <%= content_tag :div, class: 'space-y-12 mt-12' do %>
@@ -26,7 +26,7 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
26
26
 
27
27
  def back_path
28
28
  if via_resource?
29
- helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
29
+ helpers.resource_path(model: association_resource.model_class, resource: association_resource, resource_id: params[:via_resource_id])
30
30
  else
31
31
  helpers.resources_path(resource: @resource)
32
32
  end
@@ -48,11 +48,19 @@ module Avo
48
48
  end
49
49
 
50
50
  def set_action
51
- action_class = params[:action_id].gsub("avo_actions_", "").camelize.safe_constantize
52
-
53
51
  @action = action_class.new(model: @model, resource: @resource, user: _current_user, view: :edit)
54
52
  end
55
53
 
54
+ def action_class
55
+ klass_name = params[:action_id].gsub("avo_actions_", "").camelize
56
+
57
+ valid_klass = Avo::BaseAction.descendants.find do |action|
58
+ action.to_s == klass_name
59
+ end
60
+
61
+ valid_klass
62
+ end
63
+
56
64
  def respond(response)
57
65
  response[:type] ||= :reload
58
66
  messages = get_messages response
@@ -143,10 +143,12 @@ module Avo
143
143
  end
144
144
 
145
145
  def set_related_model
146
+ association_name = BaseResource.valid_association_name(@model, params[:related_name])
147
+
146
148
  @related_model = if @field.is_a? Avo::Fields::HasOneField
147
- @model.send params[:related_name]
149
+ @model.send association_name
148
150
  else
149
- eager_load_files(@related_resource, @model.send(params[:related_name])).find params[:related_id]
151
+ eager_load_files(@related_resource, @model.send(association_name)).find params[:related_id]
150
152
  end
151
153
  end
152
154