avo 2.20.0 → 2.21.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/app/components/avo/empty_state_component.html.erb +1 -1
  4. data/app/components/avo/empty_state_component.rb +12 -10
  5. data/app/components/avo/fields/belongs_to_field/edit_component.rb +1 -1
  6. data/app/components/avo/fields/boolean_field/edit_component.html.erb +1 -1
  7. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +1 -1
  8. data/app/components/avo/fields/code_field/edit_component.html.erb +2 -2
  9. data/app/components/avo/fields/country_field/edit_component.html.erb +1 -1
  10. data/app/components/avo/fields/date_field/edit_component.html.erb +2 -2
  11. data/app/components/avo/fields/date_time_field/edit_component.html.erb +2 -2
  12. data/app/components/avo/fields/edit_component.rb +4 -0
  13. data/app/components/avo/fields/external_image_field/edit_component.html.erb +1 -1
  14. data/app/components/avo/fields/file_field/edit_component.html.erb +1 -1
  15. data/app/components/avo/fields/files_field/edit_component.html.erb +1 -1
  16. data/app/components/avo/fields/has_one_field/show_component.html.erb +3 -3
  17. data/app/components/avo/fields/markdown_field/edit_component.html.erb +1 -1
  18. data/app/components/avo/fields/markdown_field/show_component.html.erb +1 -1
  19. data/app/components/avo/fields/number_field/edit_component.html.erb +1 -1
  20. data/app/components/avo/fields/password_field/edit_component.html.erb +1 -1
  21. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +1 -1
  22. data/app/components/avo/fields/select_field/edit_component.html.erb +1 -1
  23. data/app/components/avo/fields/show_component.rb +4 -0
  24. data/app/components/avo/fields/status_field/edit_component.html.erb +1 -1
  25. data/app/components/avo/fields/tags_field/edit_component.html.erb +2 -2
  26. data/app/components/avo/fields/text_field/edit_component.html.erb +1 -1
  27. data/app/components/avo/fields/textarea_field/edit_component.html.erb +1 -1
  28. data/app/components/avo/fields/time_field/edit_component.html.erb +2 -2
  29. data/app/components/avo/fields/trix_field/edit_component.html.erb +3 -2
  30. data/app/components/avo/fields/trix_field/edit_component.rb +5 -1
  31. data/app/components/avo/index/resource_grid_component.html.erb +1 -1
  32. data/app/components/avo/index/resource_table_component.rb +3 -3
  33. data/app/components/avo/paginator_component.html.erb +1 -5
  34. data/app/components/avo/paginator_component.rb +8 -0
  35. data/app/components/avo/resource_component.rb +17 -2
  36. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  37. data/app/controllers/avo/application_controller.rb +2 -2
  38. data/app/controllers/avo/associations_controller.rb +2 -1
  39. data/app/controllers/avo/attachments_controller.rb +4 -0
  40. data/app/controllers/avo/base_controller.rb +10 -4
  41. data/app/controllers/avo/dashboards/cards_controller.rb +1 -1
  42. data/app/controllers/avo/search_controller.rb +11 -5
  43. data/app/javascript/js/controllers/fields/trix_field_controller.js +6 -3
  44. data/app/javascript/js/controllers/item_select_all_controller.js +7 -4
  45. data/app/views/avo/actions/show.html.erb +1 -1
  46. data/app/views/avo/base/_multiple_select_filter.html.erb +1 -1
  47. data/app/views/avo/partials/_resource_search.html.erb +1 -1
  48. data/config/i18n-tasks.yml +1 -1
  49. data/config/initializers/pagy.rb +16 -0
  50. data/lib/avo/base_action.rb +9 -3
  51. data/lib/avo/base_card.rb +5 -2
  52. data/lib/avo/base_resource.rb +3 -2
  53. data/lib/avo/concerns/handles_field_args.rb +8 -1
  54. data/lib/avo/fields/base_field.rb +2 -1
  55. data/lib/avo/fields/concerns/is_disabled.rb +19 -0
  56. data/lib/avo/fields/select_field.rb +1 -1
  57. data/lib/avo/filters/base_filter.rb +1 -2
  58. data/lib/avo/hosts/resource_record_host.rb +7 -0
  59. data/lib/avo/hosts/visibility_host.rb +0 -1
  60. data/lib/avo/services/authorization_service.rb +2 -2
  61. data/lib/avo/version.rb +1 -1
  62. data/lib/generators/avo/templates/field/components/edit_component.html.erb.tt +1 -1
  63. data/lib/generators/avo/templates/locales/avo.en.yml +3 -2
  64. data/lib/generators/avo/templates/locales/avo.fr.yml +3 -2
  65. data/lib/generators/avo/templates/locales/avo.nb.yml +3 -2
  66. data/lib/generators/avo/templates/locales/avo.nn.yml +3 -2
  67. data/lib/generators/avo/templates/locales/avo.pt-BR.yml +3 -2
  68. data/lib/generators/avo/templates/locales/avo.pt.yml +120 -0
  69. data/lib/generators/avo/templates/locales/avo.ro.yml +56 -55
  70. data/lib/generators/avo/templates/locales/avo.tr.yml +3 -2
  71. data/lib/generators/avo/templates/locales/pagy/nn.yml +15 -0
  72. data/lib/generators/avo/templates/locales/pagy/ro.yml +17 -0
  73. data/public/avo-assets/avo.base.js +2 -2
  74. data/public/avo-assets/avo.base.js.map +2 -2
  75. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 032f3c926677645e9939a30205693425f4c44c4db874d7e30849c600e13be8a1
4
- data.tar.gz: 110d46843bf447a9e1a1215e3d4e17e4c8bf64ca4f88889d72a504c6b9cef06e
3
+ metadata.gz: 302280e8ea8c09012d0f68c72b017a8e903261346e02fdb8b4eaf2daa3160d39
4
+ data.tar.gz: eef2ab4ee6fd1576311d85ef6ae91add6f655f9762e3cc736f39bbb2f039c31e
5
5
  SHA512:
6
- metadata.gz: feed3402af12295a712d16f45ced2248c6fcb0663cdad1d4e3b160f9d706e26322ce2966f64f1f6d0fe622f13f2019be8b12d8c16afa7f009ddefe175fd0a7f5
7
- data.tar.gz: 28c50de8194d7c649b2c629c988511f1470b00dbaa5d7a60013f743959fecff0b7e97e5c5383548552e412bdea05b9224910c38d681ddbefea25116ade529145
6
+ metadata.gz: bdf810fecb08f2a6d8a157238b28df1141b396b1a62387a88bb6094d0ac4c39195d36c319751eb7dca1368ed6f5174a241a23279bf558ade568091ce201d65ae
7
+ data.tar.gz: 983acf054fc7a5c8fd4f1f32b9f1a6c45e187d9c87f70a223203e8445f3d0ecd1d05eb80ac0fc02ec497604636a37a20d8ab8365a21477dbd9decc60ad1760f5
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (2.20.0)
4
+ avo (2.21.0)
5
5
  actionview (>= 6.0)
6
6
  active_link_to
7
7
  activerecord (>= 6.0)
@@ -409,7 +409,7 @@ GEM
409
409
  tzinfo (2.0.5)
410
410
  concurrent-ruby (~> 1.0)
411
411
  unicode-display_width (2.2.0)
412
- view_component (2.77.0)
412
+ view_component (2.78.0)
413
413
  activesupport (>= 5.0.0, < 8.0)
414
414
  concurrent-ruby (~> 1.0)
415
415
  method_source (~> 1.0)
@@ -4,7 +4,7 @@
4
4
  <div class="relative flex-1 flex flex-col items-center justify-center space-y-12 py-24">
5
5
  <%= helpers.svg view_type_svg, class: 'h-[250px]' %>
6
6
  <div class="text-gray-500 text-center">
7
- <%= message %>
7
+ <%= text %>
8
8
  </div>
9
9
  </div>
10
10
  <% if @add_background %>
@@ -1,24 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::EmptyStateComponent < ViewComponent::Base
4
- def initialize(message: nil, resource_name: nil, related_name: nil, view_type: :table, add_background: false)
4
+ attr_reader :message, :view_type, :add_background, :by_association
5
+
6
+ def initialize(message: nil, view_type: :table, add_background: false, by_association: false)
5
7
  @message = message
6
8
  @view_type = view_type
7
- @related_name = related_name
8
- @resource_name = resource_name
9
9
  @add_background = add_background
10
+ @by_association = by_association
10
11
  end
11
12
 
12
- def message
13
- return @message if @message.present?
14
-
15
- translation_tag = @related_name.present? ? 'avo.no_related_item_found' : 'avo.no_item_found'
16
- helpers.t translation_tag, item: @resource_name
13
+ def text
14
+ message || locale_message
17
15
  end
18
16
 
19
17
  def view_type_svg
20
- return "grid-empty-state" if @view_type.to_sym == :grid
18
+ "#{view_type}-empty-state"
19
+ end
20
+
21
+ private
21
22
 
22
- "table-empty-state"
23
+ def locale_message
24
+ helpers.t by_association ? 'avo.no_related_item_found' : 'avo.no_item_found'
23
25
  end
24
26
  end
@@ -8,7 +8,7 @@ class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
8
8
  end
9
9
 
10
10
  def disabled
11
- return true if @field.is_readonly?
11
+ return true if @field.is_readonly? || @field.is_disabled?
12
12
 
13
13
  # When visiting the record through it's association we keep the field disabled by default
14
14
  # We make an exception when the user deliberately instructs Avo to allow detaching in this scenario
@@ -4,7 +4,7 @@
4
4
  checked: @field.value,
5
5
  class: "text-lg h-4 w-4 checked:bg-primary-400 focus:checked:!bg-primary-400 #{@field.get_html(:classes, view: view, element: :input)}",
6
6
  data: @field.get_html(:data, view: view, element: :input),
7
- disabled: @field.is_readonly?,
7
+ disabled: disabled?,
8
8
  style: @field.get_html(:style, view: view, element: :input)
9
9
  %>
10
10
  </div>
@@ -20,7 +20,7 @@
20
20
  <%= check_box_tag "#{model_param_key}[#{@field.id}][]", id, checked, {
21
21
  class: "w-4 h-4 #{@field.get_html(:classes, view: view, element: :input)}",
22
22
  data: @field.get_html(:data, view: view, element: :input),
23
- disabled: @field.is_readonly?,
23
+ disabled: disabled?,
24
24
  id: "#{model_param_key}_#{@field.id}_#{id}",
25
25
  style: @field.get_html(:style, view: view, element: :input)
26
26
  } %> <%= label %>
@@ -8,12 +8,12 @@
8
8
  language: @field.language,
9
9
  theme: @field.theme,
10
10
  'tab-size': @field.tab_size,
11
- 'read-only': @field.is_readonly?,
11
+ 'read-only': disabled?,
12
12
  'indent-with-tabs': @field.indent_with_tabs,
13
13
  'line-wrapping': @field.line_wrapping,
14
14
  **@field.get_html(:data, view: view, element: :input),
15
15
  },
16
- disabled: @field.is_readonly?,
16
+ disabled: disabled?,
17
17
  placeholder: @field.placeholder,
18
18
  style: @field.get_html(:style, view: view, element: :input)
19
19
  %>
@@ -8,7 +8,7 @@
8
8
  },
9
9
  class: classes("w-full"),
10
10
  data: @field.get_html(:data, view: view, element: :input),
11
- disabled: @field.is_readonly?,
11
+ disabled: disabled?,
12
12
  style: @field.get_html(:style, view: view, element: :input),
13
13
  placeholder: @field.include_blank.present? ? nil : @field.placeholder
14
14
  %>
@@ -18,7 +18,7 @@
18
18
  placeholder: @field.placeholder,
19
19
  **@field.get_html(:data, view: view, element: :input)
20
20
  },
21
- disabled: @field.is_readonly?,
21
+ disabled: disabled?,
22
22
  placeholder: @field.placeholder,
23
23
  style: @field.get_html(:style, view: view, element: :input)
24
24
  %>
@@ -30,7 +30,7 @@
30
30
  placeholder: @field.placeholder,
31
31
  **@field.get_html(:data, view: view, element: :input)
32
32
  },
33
- disabled: @field.is_readonly?,
33
+ disabled: disabled?,
34
34
  placeholder: @field.placeholder,
35
35
  style: @field.get_html(:style, view: view, element: :input)
36
36
  %>
@@ -20,7 +20,7 @@
20
20
  placeholder: @field.placeholder,
21
21
  **@field.get_html(:data, view: view, element: :input)
22
22
  },
23
- disabled: @field.is_readonly?,
23
+ disabled: disabled?,
24
24
  placeholder: @field.placeholder,
25
25
  style: @field.get_html(:style, view: view, element: :input)
26
26
  %>
@@ -32,7 +32,7 @@
32
32
  placeholder: @field.placeholder,
33
33
  **@field.get_html(:data, view: view, element: :input)
34
34
  },
35
- disabled: @field.is_readonly?,
35
+ disabled: disabled?,
36
36
  placeholder: @field.placeholder,
37
37
  style: @field.get_html(:style, view: view, element: :input)
38
38
  %>
@@ -42,4 +42,8 @@ class Avo::Fields::EditComponent < ViewComponent::Base
42
42
  view: view
43
43
  }
44
44
  end
45
+
46
+ def disabled?
47
+ field.is_readonly? || field.is_disabled?
48
+ end
45
49
  end
@@ -2,7 +2,7 @@
2
2
  <%= @form.text_field @field.id,
3
3
  class: classes("w-full"),
4
4
  data: @field.get_html(:data, view: view, element: :input),
5
- disabled: @field.is_readonly?,
5
+ disabled: disabled?,
6
6
  placeholder: @field.placeholder,
7
7
  style: @field.get_html(:style, view: view, element: :input)
8
8
  %>
@@ -10,7 +10,7 @@
10
10
  accept: @field.accept,
11
11
  data: @field.get_html(:data, view: view, element: :input),
12
12
  direct_upload: @field.direct_upload,
13
- disabled: @field.is_readonly?,
13
+ disabled: disabled?,
14
14
  style: @field.get_html(:style, view: view, element: :input),
15
15
  class: "w-full"
16
16
  %>
@@ -7,7 +7,7 @@
7
7
  accept: @field.accept,
8
8
  data: @field.get_html(:data, view: view, element: :input),
9
9
  direct_upload: @field.direct_upload,
10
- disabled: @field.is_readonly?,
10
+ disabled: disabled?,
11
11
  multiple: true,
12
12
  style: @field.get_html(:style, view: view, element: :input),
13
13
  class: "w-full"
@@ -5,7 +5,7 @@
5
5
  <% else %>
6
6
  <%= render Avo::PanelComponent.new(name: @field.name) do |c| %>
7
7
  <% c.tools do %>
8
- <% if !@field.is_readonly? && can_attach? %>
8
+ <% if !@field.is_readonly? && !@field.is_disabled? && can_attach? %>
9
9
  <%= a_link attach_path,
10
10
  icon: 'heroicons/outline/link',
11
11
  color: :primary,
@@ -13,7 +13,7 @@
13
13
  <%= t('avo.attach_item', item: @field.name.downcase) %>
14
14
  <% end %>
15
15
  <% end %>
16
- <% if !@field.is_readonly? && can_see_the_create_button? %>
16
+ <% if !@field.is_readonly? && !@field.is_disabled? && can_see_the_create_button? %>
17
17
  <%= a_link create_path,
18
18
  icon: 'heroicons/outline/plus',
19
19
  'data-target': 'create',
@@ -27,7 +27,7 @@
27
27
 
28
28
  <% c.body do %>
29
29
  <div class="py-8 flex justify-center items-center">
30
- <%= empty_state resource_name: @field.id, related_name: params[:related_name] %>
30
+ <%= empty_state by_association: params[:related_name].present? %>
31
31
  </div>
32
32
  <% end %>
33
33
  <% end %>
@@ -7,7 +7,7 @@
7
7
  'simple-mde-target': 'element',
8
8
  'component-options': @field.options.to_json,
9
9
  },
10
- disabled: @field.is_readonly?,
10
+ disabled: disabled?,
11
11
  placeholder: @field.placeholder,
12
12
  style: @field.get_html(:style, view: view, element: :input)
13
13
  %>
@@ -3,7 +3,7 @@
3
3
  <%= text_area_tag @field.id, @field.value,
4
4
  class: helpers.input_classes('w-full js-has-simple-mde-editor'),
5
5
  placeholder: @field.placeholder,
6
- disabled: @field.is_readonly?,
6
+ disabled: disabled?,
7
7
  'data-simple-mde-target': 'element',
8
8
  'data-component-options': @field.options.to_json,
9
9
  'data-view': :show %>
@@ -2,7 +2,7 @@
2
2
  <%= @form.number_field @field.id,
3
3
  class: classes("w-full"),
4
4
  data: @field.get_html(:data, view: view, element: :input),
5
- disabled: @field.is_readonly?,
5
+ disabled: disabled?,
6
6
  max: @field.max,
7
7
  min: @field.min,
8
8
  placeholder: @field.placeholder,
@@ -2,7 +2,7 @@
2
2
  <%= @form.password_field @field.id,
3
3
  class: classes("w-full"),
4
4
  data: @field.get_html(:data, view: view, element: :input),
5
- disabled: @field.is_readonly?,
5
+ disabled: disabled?,
6
6
  placeholder: @field.placeholder,
7
7
  style: @field.get_html(:style, view: view, element: :input)
8
8
  %>
@@ -7,7 +7,7 @@
7
7
  <%= @form.range_field @field.id,
8
8
  class: "w-full #{@field.get_html(:classes, view: view, element: :input)}",
9
9
  data: @field.get_html(:data, view: view, element: :input),
10
- disabled: @field.is_readonly?,
10
+ disabled: disabled?,
11
11
  max: @field.max,
12
12
  min: 0,
13
13
  placeholder: @field.placeholder,
@@ -7,7 +7,7 @@
7
7
  },
8
8
  class: classes("w-full"),
9
9
  data: @field.get_html(:data, view: view, element: :input),
10
- disabled: @field.is_readonly?,
10
+ disabled: disabled?,
11
11
  style: @field.get_html(:style, view: view, element: :input),
12
12
  value: @field.model.present? ? @field.model[@field.id] : @field.value,
13
13
  placeholder: @field.include_blank.present? ? nil : @field.placeholder
@@ -52,4 +52,8 @@ class Avo::Fields::ShowComponent < ViewComponent::Base
52
52
  view: view
53
53
  }
54
54
  end
55
+
56
+ def disabled?
57
+ field.is_readonly? || field.is_disabled?
58
+ end
55
59
  end
@@ -2,7 +2,7 @@
2
2
  <%= @form.text_field @field.id,
3
3
  class: classes("w-full"),
4
4
  data: @field.get_html(:data, view: view, element: :input),
5
- disabled: @field.is_readonly?,
5
+ disabled: disabled?,
6
6
  placeholder: @field.placeholder,
7
7
  style: @field.get_html(:style, view: view, element: :input),
8
8
  value: @resource.model.present? ? @resource.model[@field.id] : @field.value
@@ -6,7 +6,7 @@
6
6
  data: {
7
7
  'tags-field-target': 'fakeInput',
8
8
  },
9
- disabled: @field.is_readonly?,
9
+ disabled: disabled?,
10
10
  placeholder: @field.placeholder,
11
11
  style: @field.get_html(:style, view: view, element: :input),
12
12
  value: ''
@@ -22,7 +22,7 @@
22
22
  'delimiters': @field.delimiters,
23
23
  'close-on-select': @field.close_on_select ? 1 : 0,
24
24
  },
25
- disabled: @field.is_readonly?,
25
+ disabled: disabled?,
26
26
  placeholder: @field.placeholder,
27
27
  style: @field.get_html(:style, view: view, element: :input),
28
28
  value: @field.field_value.to_json
@@ -2,7 +2,7 @@
2
2
  <%= form.text_field @field.id,
3
3
  class: classes("w-full"),
4
4
  data: @field.get_html(:data, view: view, element: :input),
5
- disabled: @field.is_readonly?,
5
+ disabled: disabled?,
6
6
  placeholder: @field.placeholder,
7
7
  style: @field.get_html(:style, view: view, element: :input),
8
8
  # value: @field.value,
@@ -2,7 +2,7 @@
2
2
  <%= @form.text_area @field.id,
3
3
  class: classes("w-full"),
4
4
  data: @field.get_html(:data, view: view, element: :input),
5
- disabled: @field.is_readonly?,
5
+ disabled: disabled?,
6
6
  placeholder: @field.placeholder,
7
7
  rows: @field.rows,
8
8
  style: @field.get_html(:style, view: view, element: :input)
@@ -20,7 +20,7 @@
20
20
  placeholder: @field.placeholder,
21
21
  **@field.get_html(:data, view: view, element: :input)
22
22
  },
23
- disabled: @field.is_readonly?,
23
+ disabled: disabled?,
24
24
  placeholder: @field.placeholder,
25
25
  style: @field.get_html(:style, view: view, element: :input)
26
26
  %>
@@ -32,7 +32,7 @@
32
32
  placeholder: @field.placeholder,
33
33
  **@field.get_html(:data, view: view, element: :input)
34
34
  },
35
- disabled: @field.is_readonly?,
35
+ disabled: disabled?,
36
36
  placeholder: @field.placeholder,
37
37
  style: @field.get_html(:style, view: view, element: :input)
38
38
  %>
@@ -1,5 +1,6 @@
1
1
  <%= field_wrapper **field_wrapper_args, full_width: true do %>
2
- <%= content_tag :div, class: "relative block overflow-x-auto max-w-full",
2
+ <%= content_tag :div,
3
+ class: "relative block overflow-x-auto max-w-full",
3
4
  data: {
4
5
  controller: "trix-field",
5
6
  trix_field_target: "controller",
@@ -24,7 +25,7 @@
24
25
  <%= @form.text_area @field.id,
25
26
  class: classes("w-full hidden"),
26
27
  data: @field.get_html(:data, view: view, element: :input),
27
- disabled: @field.is_readonly?,
28
+ disabled: disabled?,
28
29
  id: trix_id,
29
30
  placeholder: @field.placeholder,
30
31
  style: @field.get_html(:style, view: view, element: :input)
@@ -20,6 +20,10 @@ class Avo::Fields::TrixField::EditComponent < Avo::Fields::EditComponent
20
20
  end
21
21
 
22
22
  def trix_id
23
- "trix_#{resource_name}_#{@field.id}"
23
+ if resource_name.present?
24
+ "trix_#{resource_name}_#{@field.id}"
25
+ elsif form.present?
26
+ "trix_#{form.index}_#{@field.id}"
27
+ end
24
28
  end
25
29
  end
@@ -9,6 +9,6 @@
9
9
  </div>
10
10
  <% else %>
11
11
  <div class="bg-white rounded shadow-panel">
12
- <%= helpers.empty_state resource_name: @resource.name.downcase.pluralize, related_name: params[:related_name], view_type: :grid %>
12
+ <%= helpers.empty_state by_association: params[:related_name].present?, view_type: :grid %>
13
13
  </div>
14
14
  <% end %>
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Avo::Index::ResourceTableComponent < ViewComponent::Base
4
4
  include Avo::ApplicationHelper
5
- attr_reader :pagy
5
+ attr_reader :pagy, :query
6
6
 
7
7
  def initialize(resources: nil, resource: nil, reflection: nil, parent_model: nil, parent_resource: nil, pagy: nil, query: nil)
8
8
  @resources = resources
@@ -15,10 +15,10 @@ class Avo::Index::ResourceTableComponent < ViewComponent::Base
15
15
  end
16
16
 
17
17
  def encrypted_query
18
- return if @query.nil?
18
+ return :select_all_disabled if query.nil? || !query.respond_to?(:all) || !query.all.respond_to?(:to_sql)
19
19
 
20
20
  Avo::Services::EncryptionService.encrypt(
21
- message: @query.all.to_sql,
21
+ message: query.all.to_sql,
22
22
  purpose: :select_all
23
23
  )
24
24
  end
@@ -25,11 +25,7 @@
25
25
  %> <%= t('avo.per_page').downcase %>
26
26
  </div>
27
27
  <% per_page_options.each do |option| %>
28
- <% if parent_model.present? %>
29
- <%= link_to "Change to #{option} items per page", helpers.related_resources_path(parent_model, parent_model, per_page: option, keep_query_params: true, page: 1), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
30
- <% else %>
31
- <%= link_to "Change to #{option} items per page", helpers.resources_path(resource: resource, per_page: option, keep_query_params: true, page: 1), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
32
- <% end %>
28
+ <%= link_to "Change to #{option} items per page", change_items_per_page_url(option), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
33
29
  <% end %>
34
30
  </div>
35
31
  </div>
@@ -16,4 +16,12 @@ class Avo::PaginatorComponent < ViewComponent::Base
16
16
  @parent_model = parent_model
17
17
  @discreet_pagination = discreet_pagination
18
18
  end
19
+
20
+ def change_items_per_page_url(option)
21
+ if parent_model.present?
22
+ helpers.related_resources_path(parent_model, parent_model, per_page: option, keep_query_params: true, page: 1)
23
+ else
24
+ helpers.resources_path(resource: resource, per_page: option, keep_query_params: true, page: 1)
25
+ end
26
+ end
19
27
  end
@@ -53,6 +53,7 @@ class Avo::ResourceComponent < Avo::BaseComponent
53
53
  end
54
54
  end
55
55
 
56
+ # Ex: A Post has many Comments
56
57
  def authorize_association_for(policy_method)
57
58
  policy_result = true
58
59
 
@@ -67,11 +68,25 @@ class Avo::ResourceComponent < Avo::BaseComponent
67
68
 
68
69
  if association_name.present?
69
70
  method_name = "#{policy_method}_#{association_name}?".to_sym
70
- # Prepare the authorization service
71
+
72
+ # Use the policy methods from the parent (Post)
71
73
  service = reflection_resource.authorization
72
74
 
73
75
  if service.has_method?(method_name, raise_exception: false)
74
- policy_result = service.authorize_action(method_name, raise_exception: false)
76
+ # Some policy methods should get the parent record in order to have the necessarry information to do the authorization
77
+ # Example: Post->has_many->Comments
78
+ # When you want to authorize the creation/attaching of a Comment, you don't have the Comment instance.
79
+ # But you do have the Post instance and you can get that in your policy to authorize against.
80
+ parent_policy_methods = [:view, :create, :attach, :act_on]
81
+
82
+ record = if parent_policy_methods.include?(policy_method)
83
+ # Use the parent record (Post)
84
+ reflection_resource.model
85
+ else
86
+ # Override the record with the child record (Comment)
87
+ resource.model
88
+ end
89
+ policy_result = service.authorize_action(method_name, record: record, raise_exception: false)
75
90
  end
76
91
  end
77
92
  end
@@ -52,7 +52,7 @@
52
52
  <%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, pagy: @pagy, query: @query)) %>
53
53
  </div>
54
54
  <% else %>
55
- <%= helpers.empty_state resource_name: @resource.name.downcase.pluralize, related_name: params[:related_name], view_type: view_type, add_background: true %>
55
+ <%= helpers.empty_state by_association: params[:related_name].present?, view_type: view_type, add_background: true %>
56
56
  <% end %>
57
57
  <% end %>
58
58
  <% end %>
@@ -172,11 +172,11 @@ module Avo
172
172
  end
173
173
 
174
174
  def hydrate_resource
175
- @resource.hydrate(view: action_name.to_sym, user: _current_user)
175
+ @resource.hydrate(view: action_name.to_sym, user: _current_user, model: @model)
176
176
  end
177
177
 
178
178
  def hydrate_related_resource
179
- @related_resource.hydrate(view: action_name.to_sym, user: _current_user, model: @model)
179
+ @related_resource.hydrate(view: action_name.to_sym, user: _current_user, model: @related_model)
180
180
  end
181
181
 
182
182
  def authorize_base_action
@@ -3,12 +3,13 @@ require_dependency "avo/base_controller"
3
3
  module Avo
4
4
  class AssociationsController < BaseController
5
5
  before_action :set_model, only: [:show, :index, :new, :create, :destroy, :order]
6
+ before_action :hydrate_resource, only: [:show, :index, :new, :create, :destroy, :order]
6
7
  before_action :set_related_resource_name
7
8
  before_action :set_related_resource, only: [:show, :index, :new, :create, :destroy, :order]
8
9
  before_action :set_related_authorization
9
10
  before_action :set_reflection_field
10
- before_action :hydrate_related_resource, only: [:show, :index, :create, :destroy, :order]
11
11
  before_action :set_related_model, only: [:show, :order]
12
+ before_action :hydrate_related_resource, only: [:show, :index, :create, :destroy, :order]
12
13
  before_action :set_reflection
13
14
  before_action :set_attachment_class, only: [:show, :index, :new, :create, :destroy, :order]
14
15
  before_action :set_attachment_resource, only: [:show, :index, :new, :create, :destroy, :order]
@@ -10,6 +10,10 @@ module Avo
10
10
  blob = ActiveStorage::Blob.create_and_upload! io: params[:file], filename: params[:filename]
11
11
  association_name = BaseResource.valid_attachment_name(@model, params[:attachment_key])
12
12
 
13
+ if association_name.blank?
14
+ raise ActionController::BadRequest.new("Could not find the attachment association for #{params[:attachment_key]} (check the `attachment_key` for this Trix field)")
15
+ end
16
+
13
17
  @model.send(association_name).attach blob
14
18
 
15
19
  render json: {
@@ -14,6 +14,7 @@ module Avo
14
14
  before_action :fill_model, only: [:create, :update]
15
15
  # Don't run base authorizations for associations
16
16
  before_action :authorize_base_action, if: -> { controller_name != "associations" }
17
+ before_action :set_pagy_locale, only: :index
17
18
 
18
19
  def index
19
20
  @page_title = @resource.plural_name.humanize
@@ -223,11 +224,11 @@ module Avo
223
224
  # In case there's an error somewhere else than the model
224
225
  # Example: When you save a license that should create a user for it and creating that user throws and error.
225
226
  # Example: When you Try to delete a record and has a foreign key constraint.
226
- @errors = Array.wrap(exception.message)
227
+ exception_message = exception.message
227
228
  end
228
229
 
229
230
  # Add the errors from the model
230
- @errors = Array.wrap([@errors, @model.errors.full_messages]).compact
231
+ @errors = @model.errors.full_messages.reject { |error| exception_message.include? error }.unshift exception_message
231
232
 
232
233
  succeeded
233
234
  end
@@ -315,7 +316,7 @@ module Avo
315
316
  filter[:class].new arguments: filter[:arguments]
316
317
  end
317
318
  .select do |filter|
318
- filter.visible_in_view(resource: @resource, parent_model: @parent_model, parent_resource: @parent_resource)
319
+ filter.visible_in_view(resource: @resource, parent_resource: @parent_resource)
319
320
  end
320
321
  end
321
322
 
@@ -326,7 +327,7 @@ module Avo
326
327
  action[:class].new(model: @model, resource: @resource, view: @view, arguments: action[:arguments])
327
328
  end
328
329
  .select do |action|
329
- action.visible_in_view(parent_model: @parent_model, parent_resource: @parent_resource)
330
+ action.visible_in_view(parent_resource: @parent_resource)
330
331
  end
331
332
  end
332
333
 
@@ -510,5 +511,10 @@ module Avo
510
511
  def is_associated_record?
511
512
  params[:via_relation_class].present? && params[:via_resource_id].present?
512
513
  end
514
+
515
+ # Set pagy locale from params or from avo configuration, if both nil locale = "en"
516
+ def set_pagy_locale
517
+ @pagy_locale = locale.to_s || Avo.configuration.locale || "en"
518
+ end
513
519
  end
514
520
  end
@@ -21,7 +21,7 @@ module Avo
21
21
 
22
22
  def set_card
23
23
  @card = @dashboard.item_at_index(params[:index].to_i).tap do |card|
24
- card.hydrate(dashboard: @dashboard, params: params)
24
+ card.hydrate(dashboard: @dashboard)
25
25
  end
26
26
  end
27
27