avo 1.18.2 → 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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +6 -6
  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/trix_field/edit_component.html.erb +1 -1
  13. data/app/components/avo/index/grid_item_component.html.erb +4 -4
  14. data/app/components/avo/index/resource_controls_component.html.erb +1 -1
  15. data/app/components/avo/index/resource_controls_component.rb +22 -6
  16. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  17. data/app/components/avo/panel_component.rb +3 -4
  18. data/app/components/avo/resource_component.rb +5 -1
  19. data/app/components/avo/views/resource_edit_component.html.erb +2 -2
  20. data/app/components/avo/views/resource_edit_component.rb +3 -3
  21. data/app/components/avo/views/resource_index_component.html.erb +2 -8
  22. data/app/components/avo/views/resource_index_component.rb +7 -15
  23. data/app/components/avo/views/resource_new_component.html.erb +2 -2
  24. data/app/components/avo/views/resource_new_component.rb +6 -2
  25. data/app/components/avo/views/resource_show_component.html.erb +5 -4
  26. data/app/components/avo/views/resource_show_component.rb +11 -6
  27. data/app/controllers/avo/actions_controller.rb +1 -1
  28. data/app/controllers/avo/application_controller.rb +2 -61
  29. data/app/controllers/avo/attachments_controller.rb +3 -2
  30. data/app/controllers/avo/base_controller.rb +28 -21
  31. data/app/controllers/avo/home_controller.rb +5 -1
  32. data/app/controllers/avo/relations_controller.rb +3 -3
  33. data/app/controllers/avo/search_controller.rb +1 -1
  34. data/app/helpers/avo/application_helper.rb +5 -11
  35. data/app/helpers/avo/resources_helper.rb +1 -1
  36. data/app/helpers/avo/url_helpers.rb +77 -0
  37. data/app/packs/entrypoints/application.css +1 -0
  38. data/app/packs/entrypoints/application.js +7 -0
  39. data/app/packs/js/controllers/loading_button_controller.js +22 -0
  40. data/app/packs/stylesheets/components/status.css +22 -15
  41. data/app/packs/stylesheets/spinner.css +49 -0
  42. data/app/views/avo/actions/show.html.erb +2 -2
  43. data/app/views/avo/base/_actions.html.erb +3 -3
  44. data/app/views/avo/base/_filters.html.erb +1 -1
  45. data/app/views/avo/home/failed_to_load.html.erb +3 -0
  46. data/app/views/avo/partials/_failed_state.html.erb +16 -0
  47. data/app/views/avo/partials/_paginator.html.erb +2 -2
  48. data/app/views/avo/relations/new.html.erb +0 -2
  49. data/app/views/avo/sidebar/_sidebar.html.erb +1 -1
  50. data/app/views/kaminari/_page.html.erb +1 -1
  51. data/config/routes.rb +7 -5
  52. data/lib/avo/app.rb +4 -8
  53. data/lib/avo/base_action.rb +9 -1
  54. data/lib/avo/base_resource.rb +33 -7
  55. data/lib/avo/configuration.rb +2 -0
  56. data/lib/avo/fields/base_field.rb +8 -2
  57. data/lib/avo/fields/has_and_belongs_to_many_field.rb +1 -19
  58. data/lib/avo/fields/has_base_field.rb +35 -0
  59. data/lib/avo/fields/has_many_field.rb +1 -19
  60. data/lib/avo/fields/has_one_field.rb +3 -19
  61. data/lib/avo/services/authorization_service.rb +8 -4
  62. data/lib/avo/version.rb +1 -1
  63. data/lib/generators/avo/templates/locales/avo.en.yml +2 -0
  64. data/public/avo-packs/css/{application-f9191617.css → application-c3b50b28.css} +54 -12
  65. data/public/avo-packs/css/application-c3b50b28.css.br +0 -0
  66. data/public/avo-packs/css/{application-f9191617.css.gz → application-c3b50b28.css.gz} +0 -0
  67. data/public/avo-packs/css/application-c3b50b28.css.map +1 -0
  68. data/public/avo-packs/css/application-c3b50b28.css.map.br +0 -0
  69. data/public/avo-packs/css/application-c3b50b28.css.map.gz +0 -0
  70. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js +2 -0
  71. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.br +0 -0
  72. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.gz +0 -0
  73. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map +1 -0
  74. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.br +0 -0
  75. data/public/avo-packs/js/actioncable-7119dbc1a908641fb263.chunk.js.map.gz +0 -0
  76. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js +26 -0
  77. data/public/avo-packs/js/{application-cc89f096028eb1d4d971.js.LICENSE.txt → application-6fc968cfa52976c4582b.js.LICENSE.txt} +0 -0
  78. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.br +0 -0
  79. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.gz +0 -0
  80. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map +1 -0
  81. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.br +0 -0
  82. data/public/avo-packs/js/application-6fc968cfa52976c4582b.js.map.gz +0 -0
  83. data/public/avo-packs/manifest.json +21 -21
  84. metadata +27 -21
  85. data/public/avo-packs/css/application-f9191617.css.br +0 -0
  86. data/public/avo-packs/css/application-f9191617.css.map +0 -1
  87. data/public/avo-packs/css/application-f9191617.css.map.br +0 -0
  88. data/public/avo-packs/css/application-f9191617.css.map.gz +0 -0
  89. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js +0 -2
  90. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.br +0 -0
  91. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.gz +0 -0
  92. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map +0 -1
  93. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.br +0 -0
  94. data/public/avo-packs/js/219-9aa2b689f44613118203.chunk.js.map.gz +0 -0
  95. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js +0 -26
  96. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.br +0 -0
  97. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.gz +0 -0
  98. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map +0 -1
  99. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.br +0 -0
  100. data/public/avo-packs/js/application-cc89f096028eb1d4d971.js.map.gz +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a9e6624587295b46f8f052da4cb2b6ffb8faff958d1de9ab0fe2eb98a58c05c
4
- data.tar.gz: 1010b48e463a388d0b20e3348de1c49f8468d63c2a14d78fe9ca4a9ad5c87e3e
3
+ metadata.gz: fe6f8fd6152562419ea4552e39ad441375f04932f9c3e15d4a46562d815a85a3
4
+ data.tar.gz: 87a5b8936878970adb4be91743166f4fb33105d13110e517848dc865eb606157
5
5
  SHA512:
6
- metadata.gz: d18de447e0138f28d1ceb806bf16968b88dc81c0f1b097fee892bda8b19259a8df939c3fe3060e963a0f1f04e2696f3706b1f68a42b92c2e1f53a48e09788518
7
- data.tar.gz: 91d9ec101ce25c6dba5300a054476ef8fef432cfe18d6140b0f6ff92a73be95fca213a549af6c38bfd7e3f392822ad6feaeea2fff77f208395137f063e3b7594
6
+ metadata.gz: 65b14a78a7f769504202072693f7b5f648a798989d7e74e7aad0d4909d610cd2ffc4092a39fbe69d8cabdc7ffb69b0d350fac3cb93951f6dc27db9ffd5678ca0
7
+ data.tar.gz: ca87e17c0fcb5aeee675aee252fa1d872aedab05109981410198376c77b8d79e8159d566bb2db9f0fec1952c1c098113a45fa1e8cbb5fc321c4864f0e80ba57a
data/Gemfile CHANGED
@@ -15,7 +15,7 @@ gemspec
15
15
  # gem 'byebug', group: [:development, :test]
16
16
 
17
17
  # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
18
- gem "webpacker", "6.0.0.rc.6"
18
+ gem "webpacker", "6.0.0.rc.2"
19
19
 
20
20
  gem "countries"
21
21
 
@@ -91,7 +91,7 @@ group :test do
91
91
  gem "rspec-rails", "~> 4.0.0"
92
92
  gem "rails-controller-testing"
93
93
  # Adds support for Capybara system testing and selenium driver
94
- gem "capybara", ">= 2.15"
94
+ gem "capybara", "3.36"
95
95
  gem "selenium-webdriver"
96
96
  # Easy installation and use of web drivers to run system tests with browsers
97
97
  gem "webdrivers"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (1.18.2)
4
+ avo (1.19.0)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -224,7 +224,7 @@ GEM
224
224
  net-protocol
225
225
  timeout
226
226
  nio4r (2.5.8)
227
- nokogiri (1.13.0)
227
+ nokogiri (1.13.1)
228
228
  mini_portile2 (~> 2.7.0)
229
229
  racc (~> 1.4)
230
230
  orm_adapter (0.5.0)
@@ -394,7 +394,7 @@ GEM
394
394
  addressable (>= 2.8.0)
395
395
  crack (>= 0.3.2)
396
396
  hashdiff (>= 0.4.0, < 2.0.0)
397
- webpacker (6.0.0.rc.6)
397
+ webpacker (6.0.0.rc.2)
398
398
  activesupport (>= 5.2)
399
399
  rack-proxy (>= 0.6.1)
400
400
  railties (>= 5.2)
@@ -420,7 +420,7 @@ DEPENDENCIES
420
420
  breadcrumbs_on_rails
421
421
  bump
422
422
  byebug
423
- capybara (>= 2.15)
423
+ capybara (= 3.36)
424
424
  countries
425
425
  database_cleaner
426
426
  devise
@@ -461,8 +461,8 @@ DEPENDENCIES
461
461
  web-console (>= 3.3.0)
462
462
  webdrivers
463
463
  webmock
464
- webpacker (= 6.0.0.rc.6)
464
+ webpacker (= 6.0.0.rc.2)
465
465
  zeitwerk (~> 2.3)
466
466
 
467
467
  BUNDLED WITH
468
- 2.2.32
468
+ 2.3.5
@@ -1,3 +1,3 @@
1
1
  <%= index_field_wrapper field: @field do %>
2
- <%= link_to @field.label, helpers.resource_path(@field.value) %>
2
+ <%= link_to @field.label, helpers.resource_path(model: @field.value, resource: @field.target_resource) %>
3
3
  <% end %>
@@ -1,3 +1,3 @@
1
1
  <%= show_field_wrapper field: @field, index: @index do %>
2
- <%= link_to @field.label, helpers.resource_path(@field.value, via_resource_class: @resource.model_class, via_resource_id: @resource.model.id) %>
2
+ <%= link_to @field.label, helpers.resource_path(model: @field.value, resource: @field.target_resource, via_resource_class: @resource.model_class, via_resource_id: @resource.model.id) %>
3
3
  <% end %>
@@ -23,7 +23,7 @@
23
23
  </div>
24
24
  <div>
25
25
  <% if @resource.authorization.authorize_action(:delete_attachments?, raise_exception: false) %>
26
- <%= a_link "#{Avo::App.root_path}/resources/#{@resource.model.model_name.route_key}/#{@resource.model.id}/active_storage_attachments/#{@id}/#{@file.id}", color: :red, variant: :outlined, size: :xs, class: 'text-center', 'data-turbo-frame': 'destroy_attachment_form', data: { confirm: t('avo.are_you_sure')} do %>
26
+ <%= a_link "#{@resource.record_path}/active_storage_attachments/#{@id}/#{@file.id}", color: :red, variant: :outlined, size: :xs, class: 'text-center', 'data-turbo-frame': 'destroy_attachment_form', data: { confirm: t('avo.are_you_sure')} do %>
27
27
  <%= helpers.svg 'trash' %> <span class="hidden lg:block lg:flex-1"><%= t 'avo.delete_file', item: @file.filename %></span>
28
28
  <% end %>
29
29
  <% end %>
@@ -23,8 +23,7 @@
23
23
  </div>
24
24
  <div>
25
25
  <% if @resource.authorization.authorize_action(:delete_attachments?, raise_exception: false) %>
26
- <%= a_link "#{Avo::App.root_path}/resources/#{@resource.model.model_name.route_key}/#{@resource.model.id
27
- }/active_storage_attachments/#{@id}/#{@file.blob_id}", color: :red, variant: :outlined, size: :md, class: '', 'data-turbo-frame': 'destroy_attachment_form', data: { confirm: t('avo.are_you_sure')} do %>
26
+ <%= a_link "#{@resource.record_path}/active_storage_attachments/#{@id}/#{@file.blob_id}", color: :red, variant: :outlined, size: :md, class: '', 'data-turbo-frame': 'destroy_attachment_form', data: { confirm: t('avo.are_you_sure')} do %>
28
27
  <%= helpers.svg 'trash' %> <%= t 'avo.delete_file', item: @file.filename %>
29
28
  <% end %>
30
29
  <% end %>
@@ -1,3 +1,3 @@
1
1
  <%= index_field_wrapper field: @field do %>
2
- <%= link_to @field.label, helpers.resource_path(@field.value) %>
2
+ <%= link_to @field.label, helpers.resource_path(model: @field.value, resource: @field.target_resource) %>
3
3
  <% end %>
@@ -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
@@ -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'),
@@ -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, scope: @resource.form_scope, 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
@@ -3,6 +3,7 @@ module Avo
3
3
  include Pundit
4
4
  include Pagy::Backend
5
5
  include Avo::ApplicationHelper
6
+ include Avo::UrlHelpers
6
7
 
7
8
  protect_from_forgery with: :exception
8
9
  before_action :init_app
@@ -19,7 +20,7 @@ module Avo
19
20
  add_flash_types :info, :warning, :success, :error
20
21
 
21
22
  def init_app
22
- 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
23
24
 
24
25
  @license = Avo::App.license
25
26
  end
@@ -70,66 +71,6 @@ module Avo
70
71
  instance_eval(&Avo.configuration.context)
71
72
  end
72
73
 
73
- def resources_path(model, keep_query_params: false, **args)
74
- return if model.nil?
75
-
76
- model_class = get_model_class model
77
-
78
- existing_params = {}
79
-
80
- begin
81
- if keep_query_params
82
- existing_params = Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
83
- end
84
- rescue; end
85
- avo.send :"resources_#{model_class.base_class.model_name.route_key}_path", **existing_params, **args
86
- end
87
-
88
- def related_resources_path(parent_model, model, keep_query_params: false, **args)
89
- return if model.nil?
90
-
91
- existing_params = {}
92
-
93
- begin
94
- if keep_query_params
95
- existing_params = Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
96
- end
97
- rescue; end
98
- Addressable::Template.new("#{Avo::App.root_path}/resources/#{@parent_resource.model.model_name.route_key}/#{@parent_resource.model.id}/#{@resource.route_key}{?query*}")
99
- .expand({query: {**existing_params, **args}})
100
- .to_str
101
- end
102
-
103
- def resource_path(model = nil, resource_id: nil, keep_query_params: false, **args)
104
- return avo.send :"resources_#{singular_name(model)}_path", resource_id, **args if resource_id.present?
105
-
106
- avo.send :"resources_#{singular_name(model)}_path", model, **args
107
- end
108
-
109
- def resource_attach_path(model_name, model_id, related_name, related_id = nil)
110
- path = "#{Avo::App.root_path}/resources/#{model_name}/#{model_id}/#{related_name}/new"
111
-
112
- path += "/#{related_id}" if related_id.present?
113
-
114
- path
115
- end
116
-
117
- def resource_detach_path(model_name, model_id, related_name, related_id = nil)
118
- path = "#{Avo::App.root_path}/resources/#{model_name}/#{model_id}/#{related_name}"
119
-
120
- path += "/#{related_id}" if related_id.present?
121
-
122
- path
123
- end
124
-
125
- def new_resource_path(model, **args)
126
- avo.send :"new_resources_#{singular_name(model)}_path", **args
127
- end
128
-
129
- def edit_resource_path(model, **args)
130
- avo.send :"edit_resources_#{singular_name(model)}_path", model.id, **args
131
- end
132
-
133
74
  private
134
75
 
135
76
  def set_resource_name