avo 1.18.0 → 1.19.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -3
  3. data/Gemfile.lock +195 -169
  4. data/app/components/avo/fields/belongs_to_field/index_component.html.erb +1 -1
  5. data/app/components/avo/fields/belongs_to_field/show_component.html.erb +1 -1
  6. data/app/components/avo/fields/common/multiple_file_viewer_component.html.erb +1 -1
  7. data/app/components/avo/fields/common/single_file_viewer_component.html.erb +1 -2
  8. data/app/components/avo/fields/has_one_field/index_component.html.erb +1 -1
  9. data/app/components/avo/fields/has_one_field/show_component.html.erb +1 -1
  10. data/app/components/avo/fields/has_one_field/show_component.rb +6 -1
  11. data/app/components/avo/fields/index_component.rb +2 -2
  12. data/app/components/avo/fields/text_field/index_component.html.erb +5 -1
  13. data/app/components/avo/fields/text_field/show_component.html.erb +5 -1
  14. data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -1
  15. data/app/components/avo/index/grid_item_component.html.erb +4 -4
  16. data/app/components/avo/index/resource_controls_component.html.erb +1 -1
  17. data/app/components/avo/index/resource_controls_component.rb +23 -7
  18. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  19. data/app/components/avo/panel_component.rb +3 -4
  20. data/app/components/avo/resource_component.rb +5 -1
  21. data/app/components/avo/views/resource_edit_component.html.erb +2 -2
  22. data/app/components/avo/views/resource_edit_component.rb +3 -3
  23. data/app/components/avo/views/resource_index_component.html.erb +2 -8
  24. data/app/components/avo/views/resource_index_component.rb +7 -15
  25. data/app/components/avo/views/resource_new_component.html.erb +2 -2
  26. data/app/components/avo/views/resource_new_component.rb +6 -2
  27. data/app/components/avo/views/resource_show_component.html.erb +5 -4
  28. data/app/components/avo/views/resource_show_component.rb +11 -6
  29. data/app/controllers/avo/actions_controller.rb +1 -1
  30. data/app/controllers/avo/application_controller.rb +4 -77
  31. data/app/controllers/avo/attachments_controller.rb +3 -2
  32. data/app/controllers/avo/base_controller.rb +28 -21
  33. data/app/controllers/avo/home_controller.rb +5 -1
  34. data/app/controllers/avo/relations_controller.rb +3 -3
  35. data/app/controllers/avo/search_controller.rb +1 -1
  36. data/app/helpers/avo/application_helper.rb +12 -0
  37. data/app/helpers/avo/resources_helper.rb +1 -1
  38. data/app/helpers/avo/url_helpers.rb +77 -0
  39. data/app/packs/entrypoints/application.css +1 -0
  40. data/app/packs/entrypoints/application.js +7 -0
  41. data/app/packs/js/controllers/loading_button_controller.js +22 -0
  42. data/app/packs/stylesheets/components/status.css +22 -15
  43. data/app/packs/stylesheets/spinner.css +49 -0
  44. data/app/views/avo/actions/show.html.erb +2 -2
  45. data/app/views/avo/base/_actions.html.erb +3 -3
  46. data/app/views/avo/base/_filters.html.erb +1 -1
  47. data/app/views/avo/home/failed_to_load.html.erb +3 -0
  48. data/app/views/avo/partials/_failed_state.html.erb +16 -0
  49. data/app/views/avo/partials/_paginator.html.erb +2 -2
  50. data/app/views/avo/partials/_profile_dropdown.html.erb +7 -5
  51. data/app/views/avo/relations/new.html.erb +0 -2
  52. data/app/views/avo/sidebar/_sidebar.html.erb +1 -1
  53. data/app/views/kaminari/_page.html.erb +1 -1
  54. data/config/routes.rb +7 -5
  55. data/db/factories.rb +9 -0
  56. data/lib/avo/app.rb +12 -12
  57. data/lib/avo/base_action.rb +9 -1
  58. data/lib/avo/base_resource.rb +41 -8
  59. data/lib/avo/configuration.rb +4 -0
  60. data/lib/avo/fields/base_field.rb +8 -2
  61. data/lib/avo/fields/files_field.rb +0 -2
  62. data/lib/avo/fields/has_and_belongs_to_many_field.rb +1 -19
  63. data/lib/avo/fields/has_base_field.rb +35 -0
  64. data/lib/avo/fields/has_many_field.rb +1 -19
  65. data/lib/avo/fields/has_one_field.rb +3 -19
  66. data/lib/avo/fields/text_field.rb +2 -0
  67. data/lib/avo/services/authorization_service.rb +8 -4
  68. data/lib/avo/version.rb +1 -1
  69. data/lib/generators/avo/templates/locales/avo.en.yml +2 -0
  70. data/public/avo-packs/css/{application-f9191617.css → application-c3b50b28.css} +54 -12
  71. data/public/avo-packs/css/application-c3b50b28.css.br +0 -0
  72. data/public/avo-packs/css/{application-f9191617.css.gz → application-c3b50b28.css.gz} +0 -0
  73. data/public/avo-packs/css/application-c3b50b28.css.map +1 -0
  74. data/public/avo-packs/css/application-c3b50b28.css.map.br +0 -0
  75. data/public/avo-packs/css/application-c3b50b28.css.map.gz +0 -0
  76. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js +2 -0
  77. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.br +0 -0
  78. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.gz +0 -0
  79. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map +1 -0
  80. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.br +0 -0
  81. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.gz +0 -0
  82. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js +26 -0
  83. data/public/avo-packs/js/{application-cc89f096028eb1d4d971.js.LICENSE.txt → application-6fc968cfa52976c4582b.js.LICENSE.txt} +0 -0
  84. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.br +0 -0
  85. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.gz +0 -0
  86. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map +1 -0
  87. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.br +0 -0
  88. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.gz +0 -0
  89. data/public/avo-packs/manifest.json +21 -21
  90. metadata +28 -22
  91. data/public/avo-packs/css/application-f9191617.css.br +0 -0
  92. data/public/avo-packs/css/application-f9191617.css.map +0 -1
  93. data/public/avo-packs/css/application-f9191617.css.map.br +0 -0
  94. data/public/avo-packs/css/application-f9191617.css.map.gz +0 -0
  95. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js +0 -2
  96. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.br +0 -0
  97. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.gz +0 -0
  98. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map +0 -1
  99. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.br +0 -0
  100. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.gz +0 -0
  101. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js +0 -26
  102. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.br +0 -0
  103. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.gz +0 -0
  104. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map +0 -1
  105. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.br +0 -0
  106. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.gz +0 -0
@@ -6,7 +6,7 @@
6
6
  <%= render Avo::PanelComponent.new(title: @field.id.capitalize) do |c| %>
7
7
  <% c.tools do %>
8
8
  <% if !@field.readonly && can_attach? %>
9
- <%= a_link helpers.resource_attach_path(@resource.model.model_name.route_key, @resource.model.id, @field.id), color: 'indigo', 'data-turbo-frame': 'attach_modal' do %>
9
+ <%= a_link attach_path, color: 'indigo', 'data-turbo-frame': 'attach_modal' do %>
10
10
  <%= svg 'view-grid-add' %> <%= t('avo.attach_item', item: @field.id).capitalize %>
11
11
  <% end %>
12
12
  <% end %>
@@ -8,8 +8,9 @@ class Avo::Fields::HasOneField::ShowComponent < Avo::Fields::ShowComponent
8
8
  if @field.present?
9
9
  reflection_resource = @field.target_resource
10
10
  if reflection_resource.present? && @resource.present?
11
- method_name = ('attach_' + reflection_resource.model_class.model_name.singular_route_key.underscore + '?').to_sym
11
+ method_name = ("attach_#{reflection_resource.model_key}?").to_sym
12
12
  defined_policy_methods = @resource.authorization.defined_methods(@resource.model_class, raise_exception: false)
13
+
13
14
  if defined_policy_methods.present? && defined_policy_methods.include?(method_name)
14
15
  attach_policy = @resource.authorization.authorize_action(method_name, raise_exception: false)
15
16
  end
@@ -17,4 +18,8 @@ class Avo::Fields::HasOneField::ShowComponent < Avo::Fields::ShowComponent
17
18
  end
18
19
  attach_policy
19
20
  end
21
+
22
+ def attach_path
23
+ helpers.avo.resources_associations_new_path(@resource.singular_model_key, @resource.model.id, @field.id)
24
+ end
20
25
  end
@@ -12,9 +12,9 @@ class Avo::Fields::IndexComponent < ViewComponent::Base
12
12
 
13
13
  def resource_path
14
14
  if @parent_model.present?
15
- helpers.resource_path(@resource.model, via_resource_class: @parent_model.class, via_resource_id: @parent_model.id)
15
+ helpers.resource_path(model: @resource.model, resource: @resource, via_resource_class: @parent_model.class, via_resource_id: @parent_model.id)
16
16
  else
17
- helpers.resource_path(@resource.model)
17
+ helpers.resource_path(model: @resource.model, resource: @resource)
18
18
  end
19
19
  end
20
20
  end
@@ -1,3 +1,7 @@
1
1
  <%= index_field_wrapper field: @field do %>
2
- <%= link_to_if @field.link_to_resource, @field.value, resource_path %>
2
+ <% if @field.as_html %>
3
+ <%== @field.value %>
4
+ <% else %>
5
+ <%= link_to_if @field.link_to_resource, @field.value, resource_path %>
6
+ <% end %>
3
7
  <% end %>
@@ -1,3 +1,7 @@
1
1
  <%= show_field_wrapper field: @field, index: @index do %>
2
- <%= @field.value %>
2
+ <% if @field.as_html %>
3
+ <%== @field.value %>
4
+ <% else %>
5
+ <%= @field.value %>
6
+ <% end %>
3
7
  <% end %>
@@ -2,7 +2,7 @@
2
2
  <div
3
3
  data-controller="trix-field"
4
4
  data-trix-field-target="controller"
5
- data-resource-name="<%= @resource.model_class.model_name.route_key %>"
5
+ data-resource-name="<%= @resource.model_key %>"
6
6
  data-resource-id="<%= @resource.model.id %>"
7
7
  data-attachments-disabled="<%= @field.attachments_disabled %>"
8
8
  data-attachment-key="<%= @field.attachment_key %>"
@@ -7,17 +7,17 @@
7
7
  <% if cover.blank? %>
8
8
  <%= render Avo::Index::GridCoverEmptyStateComponent.new %>
9
9
  <% elsif cover.respond_to?(:to_image) && cover.to_image.present? %>
10
- <%= link_to_if cover.link_to_resource, image_tag(cover.to_image, class: 'absolute h-full w-full object-cover'), helpers.resource_path(@resource.model), class: 'absolute h-full w-full object-cover', title: title.value %>
10
+ <%= link_to_if cover.link_to_resource, image_tag(cover.to_image, class: 'absolute h-full w-full object-cover'), helpers.resource_path(model: @resource.model, resource: @resource), class: 'absolute h-full w-full object-cover', title: title.value %>
11
11
  <% elsif cover.type == 'file' %>
12
12
  <% if cover.value.attached? && cover.value.representable? %>
13
- <%= 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'), helpers.resource_path(@resource.model), class: 'absolute h-full w-full object-cover', title: title.value %>
13
+ <%= 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'), helpers.resource_path(model: @resource.model, resource: @resource), class: 'absolute h-full w-full object-cover', title: title.value %>
14
14
  <% else %>
15
15
  <div class="absolute bg-gray-100 w-full h-full">
16
16
  <%= 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' %>
17
17
  </div>
18
18
  <% end %>
19
19
  <% elsif cover.value.present? %>
20
- <%= link_to_if cover.link_to_resource, image_tag(cover.value, class: 'absolute h-full w-full object-cover'), helpers.resource_path(@resource.model), class: 'absolute h-full w-full object-cover', title: title.value %>
20
+ <%= link_to_if cover.link_to_resource, image_tag(cover.value, class: 'absolute h-full w-full object-cover'), helpers.resource_path(model: @resource.model, resource: @resource), class: 'absolute h-full w-full object-cover', title: title.value %>
21
21
  <% else %>
22
22
  <%= render Avo::Index::GridCoverEmptyStateComponent.new %>
23
23
  <% end %>
@@ -25,7 +25,7 @@
25
25
  <div class="grid grid-cols-1 place-content-between p-4 h-full">
26
26
  <div class="mb-4">
27
27
  <div class="grid font-semibold leading-tight text-lg mb-2">
28
- <%= link_to_if title.link_to_resource, title.value, helpers.resource_path(@resource.model) if title.present? %>
28
+ <%= link_to_if title.link_to_resource, title.value, helpers.resource_path(model: @resource.model, resource: @resource) if title.present? %>
29
29
  </div>
30
30
  <div class="text-sm break-words">
31
31
  <%= body.value if body.present? %>
@@ -42,7 +42,7 @@
42
42
  <% end %>
43
43
 
44
44
  <% if can_delete? %>
45
- <%= form_with url: helpers.resource_path(@resource.model), method: :delete, html: {
45
+ <%= form_with url: helpers.resource_path(model: @resource.model, resource: @resource), method: :delete, html: {
46
46
  'data-turbo-frame': params[:turbo_frame]
47
47
  } do |form| %>
48
48
  <%= form.button helpers.svg('trash', class: 'text-gray-400 h-6 hover:text-gray-600'),
@@ -11,7 +11,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
11
11
  @reflection.present? &&
12
12
  @resource.model.present? &&
13
13
  (@reflection.is_a?(::ActiveRecord::Reflection::HasManyReflection) || @reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection)) &&
14
- authorize_association_for('detach')
14
+ authorize_association_for("detach")
15
15
  end
16
16
 
17
17
  def can_edit?
@@ -23,19 +23,29 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
23
23
  end
24
24
 
25
25
  def show_path
26
+ args = {}
27
+
26
28
  if @parent_model.present?
27
- helpers.resource_path(@resource.model, via_resource_class: @parent_model.class.base_class, via_resource_id: @parent_model.id)
28
- else
29
- helpers.resource_path(@resource.model)
29
+ args = {
30
+ via_resource_class: parent_resource.model_class,
31
+ via_resource_id: @parent_model.id
32
+ }
30
33
  end
34
+
35
+ helpers.resource_path(model: @resource.model, resource: @resource, **args)
31
36
  end
32
37
 
33
38
  def edit_path
39
+ args = {}
40
+
34
41
  if @parent_model.present?
35
- helpers.edit_resource_path(@resource.model, via_resource_class: @parent_model.class.base_class, via_resource_id: @parent_model.id)
36
- else
37
- helpers.edit_resource_path(@resource.model)
42
+ args = {
43
+ via_resource_class: parent_resource.model_class,
44
+ via_resource_id: @parent_model.id
45
+ }
38
46
  end
47
+
48
+ helpers.edit_resource_path(model: @resource.model, resource: @resource, **args)
39
49
  end
40
50
 
41
51
  def singular_resource_name
@@ -45,4 +55,10 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
45
55
  @resource.singular_name.present? ? @resource.singular_name : @resource.model_class.model_name.name.downcase
46
56
  end
47
57
  end
58
+
59
+ def parent_resource
60
+ return nil if @parent_model.blank?
61
+
62
+ ::Avo::App.get_resource_by_model_name @parent_model.class
63
+ end
48
64
  end
@@ -1,6 +1,6 @@
1
1
  <div class="w-full">
2
2
  <% if @resources.present?%>
3
- <table class="w-full px-4 overflow-hidden" data-resource-name='<%= @resource.model_class.model_name.route_key %>' data-controller='item-select-all'>
3
+ <table class="w-full px-4 overflow-hidden" data-resource-name='<%= @resource.model_key %>' data-controller='item-select-all'>
4
4
  <%= render partial: 'avo/partials/table_header', locals: {fields: @resource.get_fields(reflection: @reflection)} %>
5
5
  <tbody>
6
6
  <% @resources.each_with_index do |resource, index| %>
@@ -6,19 +6,18 @@ class Avo::PanelComponent < ViewComponent::Base
6
6
  renders_one :bare_content
7
7
  renders_one :footer
8
8
 
9
- def initialize(title: nil, body_classes: nil, data: {}, display_breadcrumbs: false)
9
+ def initialize(title: nil, body_classes: nil, data: {}, display_breadcrumbs: false, index: nil)
10
10
  @title = title
11
11
  @body_classes = body_classes
12
12
  @data = data
13
13
  @display_breadcrumbs = display_breadcrumbs
14
+ @index = index
14
15
  end
15
16
 
16
17
  private
17
18
 
18
19
  def data_attributes
19
- return if @data.blank?
20
-
21
- @data.map do |key, value|
20
+ @data.merge({'panel-index': @index}).map do |key, value|
22
21
  " data-#{key}=\"#{value}\""
23
22
  end.join
24
23
  end
@@ -12,7 +12,7 @@ class Avo::ResourceComponent < ViewComponent::Base
12
12
  if @reflection.present?
13
13
  reflection_resource = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name)
14
14
  if reflection_resource.present?
15
- method_name = ("#{policy_method}_#{@reflection.name.to_s.underscore}?").to_sym
15
+ method_name = "#{policy_method}_#{@reflection.name.to_s.underscore}?".to_sym
16
16
  defined_policy_methods = reflection_resource.authorization.defined_methods(reflection_resource.model_class, raise_exception: false)
17
17
  if defined_policy_methods.present? && defined_policy_methods.include?(method_name)
18
18
  association_policy = reflection_resource.authorization.authorize_action(method_name, raise_exception: false)
@@ -28,4 +28,8 @@ class Avo::ResourceComponent < ViewComponent::Base
28
28
  def simple_relation?
29
29
  @reflection.is_a? ::ActiveRecord::Reflection::HasManyReflection
30
30
  end
31
+
32
+ def relation_resource
33
+ ::Avo::App.get_resource_by_model_name params[:via_resource_class].safe_constantize
34
+ end
31
35
  end
@@ -1,6 +1,6 @@
1
1
  <div data-model-id="<%= @resource.model.id %>">
2
2
  <% @resource.panels.each do |resource_panel| %>
3
- <%= form_with model: @resource.model, url: helpers.resource_path(@resource.model), method: :put, multipart: true do |form| %>
3
+ <%= form_with model: @resource.model, scope: @resource.form_scope, url: helpers.resource_path(model: @resource.model, resource: @resource), method: :put, multipart: true do |form| %>
4
4
  <%= hidden_field_tag :referrer, back_path if params[:via_resource_class] %>
5
5
 
6
6
  <%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: true) do |c| %>
@@ -11,7 +11,7 @@
11
11
  <% end %>
12
12
 
13
13
  <% if @resource.authorization.authorize_action :update, raise_exception: false %>
14
- <%= a_button color: 'green', variant: 'outlined', type: :submit do %>
14
+ <%= a_button color: 'green', variant: 'outlined', spinner: true, type: :submit do %>
15
15
  <%= helpers.svg 'save' %> <%= t('avo.save').capitalize %>
16
16
  <% end %>
17
17
  <% end %>
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Avo::Views::ResourceEditComponent < ViewComponent::Base
3
+ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
4
4
  include Avo::ResourcesHelper
5
5
  include Avo::ApplicationHelper
6
6
 
@@ -10,9 +10,9 @@ class Avo::Views::ResourceEditComponent < ViewComponent::Base
10
10
 
11
11
  def back_path
12
12
  if via_resource?
13
- helpers.resource_path(params[:via_resource_class].safe_constantize, resource_id: params[:via_resource_id])
13
+ helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
14
14
  else
15
- helpers.resource_path(@resource.model)
15
+ helpers.resource_path(model: @resource.model, resource: @resource)
16
16
  end
17
17
  end
18
18
 
@@ -9,12 +9,6 @@
9
9
  <% end %>
10
10
  <% end %>
11
11
 
12
- <% if can_detach? %>
13
- <%= a_link detach_path, color: 'indigo', method: :delete, data: { confirm: "Are you sure you want to detach this #{@resource.singular_name}." } do %>
14
- <%= svg 'trash' %> <%= t('avo.detach_item', item: singular_resource_name).capitalize %>
15
- <% end %>
16
- <% end %>
17
-
18
12
  <% if can_attach? %>
19
13
  <%= a_link attach_path, color: 'indigo', 'data-turbo-frame': 'attach_modal' do %>
20
14
  <%= svg 'view-grid-add' %> <%= t('avo.attach_item', item: singular_resource_name).capitalize %>
@@ -24,11 +18,11 @@
24
18
 
25
19
  <% c.body do %>
26
20
  <div class="flex justify-between pt-2 pb-2 min-h-16"
27
- data-selected-resources-name="<%= @resource.model_class.model_name.route_key %>"
21
+ data-selected-resources-name="<%= @resource.model_key %>"
28
22
  data-selected-resources="[]"
29
23
  >
30
24
  <div class="flex items-center px-6 w-64">
31
- <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.route_key} if @resource.search_query.present? %>
25
+ <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.model_class.model_name.plural} if @resource.search_query.present? %>
32
26
  </div>
33
27
  <div class="flex justify-end items-center px-6 space-x-3">
34
28
  <%= render partial: 'avo/partials/view_toggle_button', locals: { available_view_types: available_view_types, view_type: view_type, turbo_frame: @turbo_frame } if @models.present? %>
@@ -55,10 +55,6 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
55
55
  @reflection.present? && klass.is_a?(::ActiveRecord::Reflection::HasManyReflection) && !has_reflection_and_is_read_only && authorize_association_for("attach")
56
56
  end
57
57
 
58
- def can_detach?
59
- @reflection.present? && @reflection.is_a?(::ActiveRecord::Reflection::HasOneReflection) && @models.present? && !has_reflection_and_is_read_only && authorize_association_for("detach")
60
- end
61
-
62
58
  def has_reflection_and_is_read_only
63
59
  if @reflection.present? && @reflection.active_record.name && @reflection.name
64
60
  fields = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name).get_field_definitions
@@ -75,35 +71,31 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
75
71
  end
76
72
 
77
73
  def create_path
74
+ args = {}
75
+
78
76
  if @reflection.present?
79
- path_args = {
77
+ args = {
80
78
  via_relation_class: @parent_model.model_name,
81
79
  via_resource_id: @parent_model.id
82
80
  }
83
81
 
84
82
  if @reflection.inverse_of.present?
85
- path_args[:via_relation] = @reflection.inverse_of.name
83
+ args[:via_relation] = @reflection.inverse_of.name
86
84
  end
87
-
88
- helpers.new_resource_path(@resource.model_class, **path_args)
89
- else
90
- helpers.new_resource_path(@resource.model_class)
91
85
  end
86
+
87
+ helpers.new_resource_path(model: @resource.model_class, resource: @resource, **args)
92
88
  end
93
89
 
94
90
  def attach_path
95
91
  "#{Avo::App.root_path}#{request.env["PATH_INFO"]}/new"
96
92
  end
97
93
 
98
- def detach_path
99
- helpers.resource_detach_path(via_resource_name, via_resource_id, via_relation_param, @models.first.id)
100
- end
101
-
102
94
  def singular_resource_name
103
95
  if @reflection.present?
104
96
  ::Avo::App.get_resource_by_model_name(@reflection.class_name).name
105
97
  else
106
- @resource.singular_name.present? ? @resource.singular_name : @resource.model_class.model_name.name.downcase
98
+ @resource.singular_name || @resource.model_class.model_name.name.downcase
107
99
  end
108
100
  end
109
101
 
@@ -1,6 +1,6 @@
1
1
  <div>
2
2
  <% @resource.panels.each do |resource_panel| %>
3
- <%= form_with model: @resource.model, url: helpers.resources_path(@resource.model, via_relation_class: params[:via_relation_class], via_resource_id: params[:via_resource_id]), local: true, multipart: true do |form| %>
3
+ <%= form_with model: @resource.model, url: helpers.resources_path(resource: @resource, via_relation_class: params[:via_relation_class], via_resource_id: params[:via_resource_id]), local: true, multipart: true do |form| %>
4
4
  <%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: true) do |c| %>
5
5
  <% c.tools do %>
6
6
  <div class="flex justify-end space-x-2">
@@ -8,7 +8,7 @@
8
8
  <%= svg 'arrow-left' %> <%= t('avo.cancel').capitalize %>
9
9
  <% end %>
10
10
 
11
- <%= a_button color: 'green', type: :submit do %>
11
+ <%= a_button color: 'green', spinner: true, type: :submit do %>
12
12
  <%= svg 'save' %> <%= t('avo.save').capitalize %>
13
13
  <% end if @resource.authorization.authorize_action :create, raise_exception: false %>
14
14
  </div>
@@ -14,9 +14,9 @@ class Avo::Views::ResourceNewComponent < ViewComponent::Base
14
14
 
15
15
  def back_path
16
16
  if via_resource?
17
- helpers.resource_path(params[:via_relation_class].safe_constantize, resource_id: params[:via_resource_id])
17
+ helpers.resource_path(model: params[:via_relation_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
18
18
  else
19
- helpers.resources_path(@resource.model)
19
+ helpers.resources_path(resource: @resource)
20
20
  end
21
21
  end
22
22
 
@@ -25,4 +25,8 @@ class Avo::Views::ResourceNewComponent < ViewComponent::Base
25
25
  def via_resource?
26
26
  params[:via_relation_class].present? && params[:via_resource_id].present?
27
27
  end
28
+
29
+ def relation_resource
30
+ ::Avo::App.get_resource_by_model_name params[:via_relation_class].safe_constantize
31
+ end
28
32
  end
@@ -1,9 +1,9 @@
1
1
  <div data-model-id="<%= @resource.model.id %>"
2
- data-selected-resources-name="<%= @resource.model.model_name.route_key %>"
2
+ data-selected-resources-name="<%= @resource.model_key %>"
3
3
  data-selected-resources='["<%= @resource.model.id %>"]'
4
4
  >
5
- <% @resource.panels.each do |resource_panel| %>
6
- <%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: @reflection.blank?) do |c| %>
5
+ <% @resource.panels.each_with_index do |resource_panel, index| %>
6
+ <%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: @reflection.blank?, index: index) do |c| %>
7
7
  <% c.tools do %>
8
8
  <% if resource_panel[:name] == @resource.default_panel_name %>
9
9
  <%= render 'actions' %>
@@ -25,8 +25,9 @@
25
25
  <% end %>
26
26
 
27
27
  <% if @resource.authorization.authorize_action(:destroy, raise_exception: false) %>
28
- <%= form_with url: helpers.resource_path(@resource.model), method: :delete, html: { 'data-turbo-frame': params[:turbo_frame] } do |form| %>
28
+ <%= form_with url: helpers.resource_path(model: @resource.model, resource: @resource), method: :delete, html: { 'data-turbo-frame': params[:turbo_frame] } do |form| %>
29
29
  <%= a_button title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
30
+ spinner: true,
30
31
  color: 'red',
31
32
  variant: 'outlined',
32
33
  type: :submit,
@@ -15,18 +15,23 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
15
15
 
16
16
  def back_path
17
17
  if via_resource?
18
- helpers.resource_path(params[:via_resource_class].safe_constantize, resource_id: params[:via_resource_id])
18
+ helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
19
19
  else
20
- helpers.resources_path(@resource.model)
20
+ helpers.resources_path(resource: @resource)
21
21
  end
22
22
  end
23
23
 
24
24
  def edit_path
25
+ args = {}
26
+
25
27
  if via_resource?
26
- helpers.edit_resource_path(@resource.model, via_resource_class: params[:via_resource_class], via_resource_id: params[:via_resource_id])
27
- else
28
- helpers.edit_resource_path(@resource.model)
28
+ args = {
29
+ via_resource_class: params[:via_resource_class],
30
+ via_resource_id: params[:via_resource_id]
31
+ }
29
32
  end
33
+
34
+ helpers.edit_resource_path(model: @resource.model, resource: @resource, **args)
30
35
  end
31
36
 
32
37
  def detach_path
@@ -34,7 +39,7 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
34
39
  end
35
40
 
36
41
  def destroy_path
37
- helpers.resource_path(@resource.model)
42
+ helpers.resource_path(model: @resource.model, resource: @resource)
38
43
  end
39
44
 
40
45
  def can_detach?
@@ -67,7 +67,7 @@ module Avo
67
67
 
68
68
  redirect_to path, "#{response[:message_type]}": response[:message]
69
69
  elsif response[:type] == :reload
70
- redirect_back fallback_location: resources_path(@resource.model_class), "#{response[:message_type]}": response[:message]
70
+ redirect_back fallback_location: resources_path(resource: @resource), "#{response[:message_type]}": response[:message]
71
71
  end
72
72
  end
73
73
  end
@@ -2,6 +2,9 @@ module Avo
2
2
  class ApplicationController < ::ActionController::Base
3
3
  include Pundit
4
4
  include Pagy::Backend
5
+ include Avo::ApplicationHelper
6
+ include Avo::UrlHelpers
7
+
5
8
  protect_from_forgery with: :exception
6
9
  before_action :init_app
7
10
  before_action :check_avo_license
@@ -17,7 +20,7 @@ module Avo
17
20
  add_flash_types :info, :warning, :success, :error
18
21
 
19
22
  def init_app
20
- Avo::App.init request: request, context: context, root_path: avo.root_path.delete_suffix("/"), current_user: _current_user
23
+ Avo::App.init request: request, context: context, root_path: avo.root_path.delete_suffix("/"), current_user: _current_user, view_context: view_context
21
24
 
22
25
  @license = Avo::App.license
23
26
  end
@@ -68,64 +71,6 @@ module Avo
68
71
  instance_eval(&Avo.configuration.context)
69
72
  end
70
73
 
71
- def resources_path(model, keep_query_params: false, **args)
72
- return if model.nil?
73
-
74
- existing_params = {}
75
-
76
- begin
77
- if keep_query_params
78
- existing_params = Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
79
- end
80
- rescue; end
81
- avo.send :"resources_#{model.model_name.route_key}_path", **existing_params, **args
82
- end
83
-
84
- def related_resources_path(parent_model, model, keep_query_params: false, **args)
85
- return if model.nil?
86
-
87
- existing_params = {}
88
-
89
- begin
90
- if keep_query_params
91
- existing_params = Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
92
- end
93
- rescue; end
94
- Addressable::Template.new("#{Avo::App.root_path}/resources/#{@parent_resource.model.model_name.route_key}/#{@parent_resource.model.id}/#{@resource.route_key}{?query*}")
95
- .expand({query: {**existing_params, **args}})
96
- .to_str
97
- end
98
-
99
- def resource_path(model = nil, resource_id: nil, keep_query_params: false, **args)
100
- return avo.send :"resources_#{singular_name(model)}_path", resource_id, **args if resource_id.present?
101
-
102
- avo.send :"resources_#{singular_name(model)}_path", model, **args
103
- end
104
-
105
- def resource_attach_path(model_name, model_id, related_name, related_id = nil)
106
- path = "#{Avo::App.root_path}/resources/#{model_name}/#{model_id}/#{related_name}/new"
107
-
108
- path += "/#{related_id}" if related_id.present?
109
-
110
- path
111
- end
112
-
113
- def resource_detach_path(model_name, model_id, related_name, related_id = nil)
114
- path = "#{Avo::App.root_path}/resources/#{model_name}/#{model_id}/#{related_name}"
115
-
116
- path += "/#{related_id}" if related_id.present?
117
-
118
- path
119
- end
120
-
121
- def new_resource_path(model, **args)
122
- avo.send :"new_resources_#{singular_name(model)}_path", **args
123
- end
124
-
125
- def edit_resource_path(model, **args)
126
- avo.send :"edit_resources_#{singular_name(model)}_path", model.id, **args
127
- end
128
-
129
74
  private
130
75
 
131
76
  def set_resource_name
@@ -283,23 +228,5 @@ module Avo
283
228
  def on_api_path
284
229
  request.original_url.match?(/.*#{Avo::App.root_path}\/avo_api\/.*/)
285
230
  end
286
-
287
- def get_model_class(model)
288
- if model.instance_of?(Class)
289
- model
290
- else
291
- model.class
292
- end
293
- end
294
-
295
- def singular_name(model_or_class)
296
- model_class = get_model_class model_or_class
297
-
298
- if ActiveModel::Naming.uncountable? model_class
299
- model_class.model_name.route_key.singularize.gsub('_index', '')
300
- else
301
- model_class.model_name.route_key.singularize
302
- end
303
- end
304
231
  end
305
232
  end
@@ -23,13 +23,14 @@ module Avo
23
23
  def destroy
24
24
  blob = ActiveStorage::Blob.find(params[:signed_attachment_id])
25
25
  attachment = blob.attachments.find_by record_id: params[:id], record_type: @model.class.to_s
26
+ path = resource_path(model: @model, resource: @resource)
26
27
 
27
28
  if attachment.present?
28
29
  attachment.destroy
29
30
 
30
- redirect_to params[:referrer] || resource_path(@model), notice: t("avo.attachment_destroyed")
31
+ redirect_to params[:referrer] || path, notice: t("avo.attachment_destroyed")
31
32
  else
32
- redirect_back fallback_location: resource_path(@model), notice: t("avo.failed_to_find_attachment")
33
+ redirect_back fallback_location: path, notice: t("avo.failed_to_find_attachment")
33
34
  end
34
35
  end
35
36
  end