avo 2.11.1.pre.2 → 2.11.2.pre.1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/stylesheets/avo.css +1 -4
  4. data/app/assets/stylesheets/css/sidebar.css +18 -0
  5. data/app/assets/svgs/failed_to_load.svg +15 -0
  6. data/app/components/avo/common_field_wrapper_component.html.erb +4 -0
  7. data/app/components/avo/fields/has_one_field/show_component.html.erb +1 -1
  8. data/app/components/avo/fields/has_one_field/show_component.rb +7 -6
  9. data/app/components/avo/fields/text_field/index_component.html.erb +2 -0
  10. data/app/components/avo/fields/text_field/show_component.html.erb +2 -0
  11. data/app/components/avo/fields/trix_field/edit_component.rb +1 -1
  12. data/app/components/avo/index/field_wrapper_component.html.erb +4 -0
  13. data/app/components/avo/index/resource_controls_component.html.erb +3 -0
  14. data/app/components/avo/index/resource_controls_component.rb +7 -5
  15. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  16. data/app/components/avo/index/resource_table_component.rb +2 -1
  17. data/app/components/avo/index/table_row_component.html.erb +14 -5
  18. data/app/components/avo/index/table_row_component.rb +2 -1
  19. data/app/components/avo/item_switcher_component.html.erb +1 -1
  20. data/app/components/avo/resource_component.rb +11 -5
  21. data/app/components/avo/sidebar_component.html.erb +8 -2
  22. data/app/components/avo/sidebar_component.rb +9 -0
  23. data/app/components/avo/tab_switcher_component.rb +4 -0
  24. data/app/components/avo/views/resource_edit_component.rb +4 -6
  25. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  26. data/app/components/avo/views/resource_index_component.rb +5 -3
  27. data/app/components/avo/views/resource_show_component.html.erb +1 -1
  28. data/app/components/avo/views/resource_show_component.rb +3 -1
  29. data/app/controllers/avo/application_controller.rb +25 -10
  30. data/app/controllers/avo/associations_controller.rb +38 -12
  31. data/app/controllers/avo/base_controller.rb +7 -2
  32. data/app/javascript/js/controllers/fields/key_value_controller.js +3 -1
  33. data/app/javascript/js/controllers/sidebar_controller.js +49 -0
  34. data/app/javascript/js/controllers.js +2 -2
  35. data/app/views/avo/base/index.html.erb +1 -0
  36. data/app/views/avo/base/show.html.erb +7 -1
  37. data/app/views/avo/home/failed_to_load.html copy.erb +23 -0
  38. data/app/views/avo/home/failed_to_load.html.erb +9 -15
  39. data/app/views/avo/partials/_javascript.html.erb +1 -0
  40. data/app/views/avo/partials/_navbar.html.erb +5 -2
  41. data/app/views/avo/partials/_table_header.html.erb +12 -5
  42. data/app/views/layouts/avo/application.html.erb +9 -4
  43. data/bin/init +5 -0
  44. data/lib/avo/base_resource.rb +16 -1
  45. data/lib/avo/concerns/has_fields.rb +12 -3
  46. data/lib/avo/concerns/has_html_attributes.rb +1 -1
  47. data/lib/avo/concerns/model_class_constantized.rb +1 -1
  48. data/lib/avo/configuration/resource_configuration.rb +21 -0
  49. data/lib/avo/configuration.rb +2 -0
  50. data/lib/avo/fields/has_base_field.rb +11 -0
  51. data/lib/avo/fields/text_field.rb +4 -2
  52. data/lib/avo/items_holder.rb +6 -0
  53. data/lib/avo/panel_builder.rb +1 -0
  54. data/lib/avo/services/authorization_service.rb +41 -37
  55. data/lib/avo/version.rb +1 -1
  56. data/lib/avo.rb +1 -0
  57. data/public/avo-assets/avo.css +78 -16
  58. data/public/avo-assets/avo.js +70 -69
  59. data/public/avo-assets/avo.js.map +3 -3
  60. metadata +7 -3
  61. data/app/javascript/js/controllers/mobile_controller.js +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bc7baa68c6256d8c8a26e4a40be36129d1187451015bc72c0ec2be65c3acd8b
4
- data.tar.gz: 57a8ef18222e622ed7d3f45334b46c97183d10ea845b321044648cd1974faf22
3
+ metadata.gz: 60431c5efb911209dd56de78864a1c68a8a3eba305041f645213cf56ae134d39
4
+ data.tar.gz: eccc5df0754aa8a0dff6428333cb618cbe4ddeaefb0bf4e41ec7289d5338cf15
5
5
  SHA512:
6
- metadata.gz: 3df946571876e5742a86eb9314fe1122ea2838bedd0c3af3027e754df24ca20a4c9961e62c46f50a399c9265ddbf2b1f2cb9b2fd2ffb43e13bdcf6595634d71c
7
- data.tar.gz: 7d9b4560a2069d752f5e82bc66c0d0eb4e1e9df5d95261371e859c8907741ac80a43a7ba09ce5ed6f59da316723b9f1ab9dbb8de3931f98113fc5de1c619ee74
6
+ metadata.gz: 553477576dc7c80f634a2cf2278143b6ba5668af418598160a5255f52be3ad04da78adc8c52a824ac70c48230a2470dd87a96604c83071f62f68ba89b59e865f
7
+ data.tar.gz: 9d5fbd770fc8e6e8609e17a72745325df0e5056c3354e87e24a6e9c17707c42073da5dccfd87aae914f368e3c3c1546e6d83328caa6db54db2e394e0882ebe07
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (2.11.1.pre.2)
4
+ avo (2.11.2.pre.1)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -16,6 +16,7 @@
16
16
  @import './css/breadcrumbs.css';
17
17
  @import './css/search.css';
18
18
  @import './css/active-storage.css';
19
+ @import './css/sidebar.css';
19
20
  @import './css/spinner.css';
20
21
  @import './css/tags.css';
21
22
 
@@ -69,10 +70,6 @@ body {
69
70
  @apply opacity-0 translate-y-1;
70
71
  }
71
72
 
72
- .application-sidebar .active:hover,
73
- .application-sidebar .active {
74
- @apply bg-blue-100 text-blue-500;
75
- }
76
73
 
77
74
  .turbo-progress-bar {
78
75
  @apply bg-blue-400;
@@ -0,0 +1,18 @@
1
+ .application-sidebar .active:hover,
2
+ .application-sidebar .active {
3
+ @apply bg-blue-100 text-blue-500;
4
+ }
5
+
6
+ .content-area {
7
+ /* remove the left padding. */
8
+ .main-content-area {
9
+ @apply lg:pl-0;
10
+ }
11
+
12
+ &.sidebar-open {
13
+ /* Add padding to the main area to allow for the sidebar to expand. */
14
+ .main-content-area {
15
+ @apply lg:pl-64;
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,15 @@
1
+ <svg width="275" height="275" viewBox="0 0 275 275" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M137.714 274.227C213.377 274.227 274.714 212.89 274.714 137.227C274.714 61.5636 213.377 0.226593 137.714 0.226593C62.0512 0.226593 0.714233 61.5636 0.714233 137.227C0.714233 212.89 62.0512 274.227 137.714 274.227Z" fill="url(#paint0_linear_663_759)"/>
3
+ <rect x="55.5142" y="67.8133" width="164.4" height="32.88" fill="white"/>
4
+ <path d="M219.914 274.227H55.5142V97.0399C63.2629 97.0312 70.6918 93.9492 76.171 88.4701C81.6502 82.9909 84.7322 75.562 84.7409 67.8133H190.688C190.679 71.6518 191.432 75.4538 192.903 78.9994C194.374 82.5449 196.533 85.7636 199.256 88.4692C201.962 91.1929 205.181 93.353 208.727 94.8242C212.273 96.2953 216.075 97.0484 219.914 97.0399V274.227Z" fill="white"/>
5
+ <path d="M137.714 186.547C161.926 186.547 181.554 166.919 181.554 142.707C181.554 118.494 161.926 98.8666 137.714 98.8666C113.502 98.8666 93.8743 118.494 93.8743 142.707C93.8743 166.919 113.502 186.547 137.714 186.547Z" fill="#4285F4"/>
6
+ <path d="M153.214 163.373L137.714 147.874L122.214 163.373L117.048 158.207L132.548 142.707L117.048 127.207L122.214 122.041L137.714 137.54L153.214 122.041L158.381 127.207L142.881 142.707L158.381 158.207L153.214 163.373Z" fill="white"/>
7
+ <path d="M161.461 197.507H113.968C110.941 197.507 108.488 199.96 108.488 202.987C108.488 206.013 110.941 208.467 113.968 208.467H161.461C164.487 208.467 166.941 206.013 166.941 202.987C166.941 199.96 164.487 197.507 161.461 197.507Z" fill="#DFEAFB"/>
8
+ <path d="M177.901 219.427H97.5275C94.501 219.427 92.0475 221.88 92.0475 224.907C92.0475 227.933 94.501 230.387 97.5275 230.387H177.901C180.927 230.387 183.381 227.933 183.381 224.907C183.381 221.88 180.927 219.427 177.901 219.427Z" fill="#DFEAFB"/>
9
+ <defs>
10
+ <linearGradient id="paint0_linear_663_759" x1="137.714" y1="0.226593" x2="137.714" y2="274.227" gradientUnits="userSpaceOnUse">
11
+ <stop stop-color="#E3ECFA"/>
12
+ <stop offset="1" stop-color="#DAE7FF"/>
13
+ </linearGradient>
14
+ </defs>
15
+ </svg>
@@ -19,4 +19,8 @@
19
19
  <div class="flex-1 flex flex-row md:min-h-inherit py-2 <% unless @displayed_in_modal %> px-6 <% end %>">
20
20
  <%= content %>
21
21
  </div>
22
+ <% if params[:avo_debug].present? %>
23
+ <!-- Raw value: -->
24
+ <!-- <%== @field.value.inspect %> -->
25
+ <% end %>
22
26
  <% end %>
@@ -7,7 +7,7 @@
7
7
  <% c.tools do %>
8
8
  <% if !@field.readonly && can_attach? %>
9
9
  <%= a_link attach_path, icon: 'heroicons/outline/link', color: :primary, 'data-turbo-frame': 'attach_modal' do %>
10
- <%= t('avo.attach_item', item: @field.name) %>
10
+ <%= t('avo.attach_item', item: @field.name.downcase) %>
11
11
  <% end %>
12
12
  <% end %>
13
13
  <% end %>
@@ -4,19 +4,20 @@ class Avo::Fields::HasOneField::ShowComponent < Avo::Fields::ShowComponent
4
4
  include Avo::ApplicationHelper
5
5
 
6
6
  def can_attach?
7
- attach_policy = true
7
+ policy_result = true
8
+
8
9
  if @field.present?
9
10
  reflection_resource = @field.target_resource
10
11
  if reflection_resource.present? && @resource.present?
11
- method_name = ("attach_#{reflection_resource.model_key}?").to_sym
12
- defined_policy_methods = @resource.authorization.defined_methods(@resource.model_class, raise_exception: false)
12
+ method_name = "attach_#{@field.id}?".to_sym
13
13
 
14
- if defined_policy_methods.present? && defined_policy_methods.include?(method_name)
15
- attach_policy = @resource.authorization.authorize_action(method_name, raise_exception: false)
14
+ if @resource.authorization.has_method?(method_name, raise_exception: false)
15
+ policy_result = @resource.authorization.authorize_action(method_name, raise_exception: false)
16
16
  end
17
17
  end
18
18
  end
19
- attach_policy
19
+
20
+ policy_result
20
21
  end
21
22
 
22
23
  def attach_path
@@ -1,6 +1,8 @@
1
1
  <%= index_field_wrapper field: @field, resource: @resource do %>
2
2
  <% if @field.as_html %>
3
3
  <%== @field.value %>
4
+ <% elsif @field.protocol.present? %>
5
+ <%= link_to @field.value, "#{@field.protocol}:#{@field.value}" %>
4
6
  <% else %>
5
7
  <%= link_to_if @field.link_to_resource, @field.value, resource_path %>
6
8
  <% end %>
@@ -1,6 +1,8 @@
1
1
  <%= show_field_wrapper field: @field, resource: @resource, index: @index do %>
2
2
  <% if @field.as_html %>
3
3
  <%== @field.value %>
4
+ <% elsif @field.protocol.present? %>
5
+ <%= link_to @field.value, "#{@field.protocol}:#{@field.value}" %>
4
6
  <% else %>
5
7
  <%= @field.value %>
6
8
  <% end %>
@@ -2,6 +2,6 @@
2
2
 
3
3
  class Avo::Fields::TrixField::EditComponent < Avo::Fields::EditComponent
4
4
  def trix_id
5
- "trix_#{@resource.name.underscore}_#{@field.id}"
5
+ "trix_#{@resource.class_name_without_resource.underscore}_#{@field.id}"
6
6
  end
7
7
  end
@@ -17,4 +17,8 @@
17
17
  <%= content %>
18
18
  <% end %>
19
19
  <% end %>
20
+ <% if params[:avo_debug].present? %>
21
+ <!-- Raw value: -->
22
+ <!-- <%== @field.value.inspect %> -->
23
+ <% end %>
20
24
  <% end %>
@@ -5,6 +5,7 @@
5
5
  <% if can_view? %>
6
6
  <%= link_to helpers.svg('eye', class: button_classes),
7
7
  show_path,
8
+ class: 'flex items-center',
8
9
  title: t('avo.view_item', item: singular_resource_name).capitalize,
9
10
  data: {
10
11
  target: 'control:view',
@@ -17,6 +18,7 @@
17
18
  <% if can_edit? %>
18
19
  <%= link_to helpers.svg('edit', class: button_classes),
19
20
  edit_path,
21
+ class: 'flex items-center',
20
22
  title: t('avo.edit_item', item: singular_resource_name).capitalize,
21
23
  data: {
22
24
  target: 'control:edit',
@@ -31,6 +33,7 @@
31
33
  <%= form_with url: helpers.resource_detach_path(params[:resource_name], params[:id], params[:related_name], @resource.model.id),
32
34
  method: :delete,
33
35
  local: true,
36
+ class: 'flex items-center',
34
37
  html: {
35
38
  'data-turbo-frame': params[:turbo_frame]
36
39
  } do |form| %>
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
4
- def initialize(resource: nil, reflection: nil, parent_model: nil, view_type: :table)
4
+ def initialize(resource: nil, reflection: nil, parent_model: nil, parent_resource: nil, view_type: :table)
5
5
  @resource = resource
6
6
  @reflection = reflection
7
7
  @parent_model = parent_model
8
+ @parent_resource = parent_resource
8
9
  @view_type = view_type
9
10
  end
10
11
 
@@ -12,7 +13,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
12
13
  @reflection.present? &&
13
14
  @resource.model.present? &&
14
15
  is_has_many_association &&
15
- authorize_association_for("detach")
16
+ authorize_association_for(:detach)
16
17
  end
17
18
 
18
19
  def can_edit?
@@ -22,8 +23,9 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
22
23
  end
23
24
 
24
25
  def can_view?
25
- return authorize_association_for(:view) if @reflection.present?
26
+ return authorize_association_for(:show) if @reflection.present?
26
27
 
28
+ # Even if there's a @reflection object present, for show we're going to fallback to the original policy.
27
29
  @resource.authorization.authorize_action(:show, raise_exception: false)
28
30
  end
29
31
 
@@ -42,7 +44,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
42
44
 
43
45
  def edit_path
44
46
  # Add the `view` param to let Avo know where to redirect back when the user clicks the `Cancel` button.
45
- args = {via_view: 'index'}
47
+ args = {via_view: "index"}
46
48
 
47
49
  if @parent_model.present?
48
50
  args = {
@@ -73,6 +75,6 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
73
75
  end
74
76
 
75
77
  def referrer_path
76
- Avo::App.root_path(paths: ['resources', params[:resource_name], params[:id], params[:related_name]], query: request.query_parameters.to_h)
78
+ Avo::App.root_path(paths: ["resources", params[:resource_name], params[:id], params[:related_name]], query: request.query_parameters.to_h)
77
79
  end
78
80
  end
@@ -4,7 +4,7 @@
4
4
  <tbody class="divide-y">
5
5
  <% @resources.each_with_index do |resource, index| %>
6
6
  <% cache_if Avo.configuration.cache_resources_on_index_view, resource.cache_hash(@parent_model), expires_in: 1.day do %>
7
- <%= render Avo::Index::TableRowComponent.new(resource: resource, reflection: @reflection, parent_model: @parent_model) %>
7
+ <%= render Avo::Index::TableRowComponent.new(resource: resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource) %>
8
8
  <% end %>
9
9
  <% end %>
10
10
  </tbody>
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Index::ResourceTableComponent < ViewComponent::Base
4
- def initialize(resources: nil, resource: nil, reflection: nil, parent_model: nil)
4
+ def initialize(resources: nil, resource: nil, reflection: nil, parent_model: nil, parent_resource: nil)
5
5
  @resources = resources
6
6
  @resource = resource
7
7
  @reflection = reflection
8
8
  @parent_model = parent_model
9
+ @parent_resource = parent_resource
9
10
  end
10
11
  end
@@ -9,12 +9,21 @@
9
9
  </div>
10
10
  </td>
11
11
  <% end %>
12
+ <% if Avo.configuration.resource_controls_on_the_left? %>
13
+ <td class="text-right whitespace-nowrap px-2" data-control="resource-controls">
14
+ <div class="flex items-center justify-end flex-grow-0 h-full w-full">
15
+ <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, view_type: :table)) %>
16
+ </div>
17
+ </td>
18
+ <% end %>
12
19
  <% @resource.get_fields(reflection: @reflection, only_root: true).each_with_index do |field, index| %>
13
20
  <%= render field.component_for_view(:index).new(field: field, resource: @resource, index: index, parent_model: @parent_model) %>
14
21
  <% end %>
15
- <td class="text-right whitespace-nowrap px-2">
16
- <div class="flex items-center justify-end flex-grow-0 h-full w-full">
17
- <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, view_type: :table)) %>
18
- </div>
19
- </td>
22
+ <% if Avo.configuration.resource_controls_on_the_right? %>
23
+ <td class="text-right whitespace-nowrap px-2" data-control="resource-controls">
24
+ <div class="flex items-center justify-end flex-grow-0 h-full w-full">
25
+ <%= render(Avo::Index::ResourceControlsComponent.new(resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, view_type: :table)) %>
26
+ </div>
27
+ </td>
28
+ <% end %>
20
29
  </tr>
@@ -3,9 +3,10 @@
3
3
  class Avo::Index::TableRowComponent < ViewComponent::Base
4
4
  include Avo::ResourcesHelper
5
5
 
6
- def initialize(resource: nil, reflection: nil, parent_model: nil)
6
+ def initialize(resource: nil, reflection: nil, parent_model: nil, parent_resource: nil)
7
7
  @resource = resource
8
8
  @reflection = reflection
9
9
  @parent_model = parent_model
10
+ @parent_resource = parent_resource
10
11
  end
11
12
  end
@@ -1,6 +1,6 @@
1
1
  <% if item.is_tool? %>
2
2
  <% if item&.partial.present? %>
3
- <%= render item.partial, tool: item %>
3
+ <%= render item.partial, tool: item, form: @form %>
4
4
  <% end %>
5
5
  <% elsif item.is_panel? %>
6
6
  <%= render Avo::PanelComponent.new(title: item.name, description: item.description, index: index, view: view) do |c| %>
@@ -39,23 +39,29 @@ class Avo::ResourceComponent < Avo::BaseComponent
39
39
  end
40
40
 
41
41
  def authorize_association_for(policy_method)
42
- association_policy = true
42
+ policy_result = true
43
43
 
44
44
  if @reflection.present?
45
+ # Fetch the appropiate resource
45
46
  reflection_resource = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name)
47
+ # Fetch the model
48
+ # Hydrate the resource with the model if we have one
46
49
  reflection_resource.hydrate(model: @parent_model) if @parent_model.present?
47
- association_name = params["related_name"]
50
+ # Use the related_name as the base of the association
51
+ association_name = @reflection.name
48
52
 
49
53
  if association_name.present?
50
54
  method_name = "#{policy_method}_#{association_name}?".to_sym
55
+ # Prepare the authorization service
56
+ service = reflection_resource.authorization
51
57
 
52
- if reflection_resource.authorization.has_method?(method_name, raise_exception: false)
53
- association_policy = reflection_resource.authorization.authorize_action(method_name, raise_exception: false)
58
+ if service.has_method?(method_name, raise_exception: false)
59
+ policy_result = service.authorize_action(method_name, raise_exception: false)
54
60
  end
55
61
  end
56
62
  end
57
63
 
58
- association_policy
64
+ policy_result
59
65
  end
60
66
 
61
67
  def main_panel
@@ -1,6 +1,12 @@
1
1
  <div
2
- class="fixed z-[60] t-0 application-sidebar w-64 hidden lg:flex flex-1 border-r lg:border-none bg-none h-[calc(100vh-4rem)] bg-gray-25 lg:bg-transparent <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
3
- data-mobile-target="sidebar"
2
+ class="avo-sidebar fixed z-[60] t-0 application-sidebar w-64 flex-1 border-r lg:border-none bg-none h-[calc(100vh-4rem)] bg-gray-25 lg:bg-transparent <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %> <%= @sidebar_open ? 'flex' : 'hidden' %>"
3
+ data-sidebar-target="<%= stimulus_target %>"
4
+ data-transition-enter="transition ease-out duration-100"
5
+ data-transition-enter-start="transform opacity-0 -translate-x-full"
6
+ data-transition-enter-end="transform opacity-100 translate-x-0"
7
+ data-transition-leave="transition ease-in duration-75"
8
+ data-transition-leave-start="transform opacity-100 translate-x-0"
9
+ data-transition-leave-end="transform opacity-0 -translate-x-full"
4
10
  >
5
11
  <div class="flex flex-col w-full h-full">
6
12
  <div class="flex-1 flex flex-col justify-between overflow-auto h-full pt-3 scroll-shadows">
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::SidebarComponent < ViewComponent::Base
4
+ def initialize(sidebar_open: nil, for_mobile: false)
5
+ @sidebar_open = sidebar_open
6
+ @for_mobile = for_mobile
7
+ end
8
+
4
9
  def dashboards
5
10
  Avo::App.dashboards_for_navigation
6
11
  end
@@ -12,4 +17,8 @@ class Avo::SidebarComponent < ViewComponent::Base
12
17
  def tools
13
18
  Avo::App.tools_for_navigation
14
19
  end
20
+
21
+ def stimulus_target
22
+ @for_mobile ? "mobileSidebar" : "sidebar"
23
+ end
15
24
  end
@@ -80,6 +80,10 @@ class Avo::TabSwitcherComponent < Avo::BaseComponent
80
80
  visible = item.visible?
81
81
  end
82
82
 
83
+ if item.respond_to?(:authorized?)
84
+ visible = item.authorized?
85
+ end
86
+
83
87
  visible
84
88
  end
85
89
  end
@@ -22,12 +22,10 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
22
22
  helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
23
23
  elsif via_index?
24
24
  helpers.resources_path(resource: @resource)
25
- else # via resource show page
26
- if is_edit?
27
- helpers.resource_path(model: @resource.model, resource: @resource)
28
- else
29
- helpers.resources_path(resource: @resource)
30
- end
25
+ elsif is_edit? # via resource show page
26
+ helpers.resource_path(model: @resource.model, resource: @resource)
27
+ else
28
+ helpers.resources_path(resource: @resource)
31
29
  end
32
30
  end
33
31
 
@@ -51,7 +51,7 @@
51
51
  <% if @resources.present? %>
52
52
  <div class="w-full overflow-auto flex flex-col mt-0">
53
53
  <div class="relative flex-1 flex">
54
- <%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model)) %>
54
+ <%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource)) %>
55
55
  </div>
56
56
  </div>
57
57
  <% else %>
@@ -15,6 +15,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
15
15
  reflection: nil,
16
16
  turbo_frame: "",
17
17
  parent_model: nil,
18
+ parent_resource: nil,
18
19
  applied_filters: []
19
20
  )
20
21
  @resource = resource
@@ -27,6 +28,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
27
28
  @reflection = reflection
28
29
  @turbo_frame = turbo_frame
29
30
  @parent_model = parent_model
31
+ @parent_resource = parent_resource
30
32
  @applied_filters = applied_filters
31
33
  @view = :index
32
34
  end
@@ -52,7 +54,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
52
54
  # The Create button is dependent on the new? policy method.
53
55
  # The create? should be called only when the user clicks the Save button so the developers gets access to the params from the form.
54
56
  def can_see_the_create_button?
55
- return authorize_association_for("create") if @reflection.present?
57
+ return authorize_association_for(:create) if @reflection.present?
56
58
 
57
59
  @resource.authorization.authorize_action(:new, raise_exception: false) && !has_reflection_and_is_read_only
58
60
  end
@@ -60,7 +62,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
60
62
  def can_see_the_actions_button?
61
63
  return false if @actions.blank?
62
64
 
63
- return authorize_association_for("act_on") if @reflection.present?
65
+ return authorize_association_for(:act_on) if @reflection.present?
64
66
 
65
67
  @resource.authorization.authorize_action(:act_on, raise_exception: false) && !has_reflection_and_is_read_only
66
68
  end
@@ -69,7 +71,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
69
71
  klass = @reflection
70
72
  klass = @reflection.through_reflection if klass.is_a? ::ActiveRecord::Reflection::ThroughReflection
71
73
 
72
- @reflection.present? && klass.is_a?(::ActiveRecord::Reflection::HasManyReflection) && !has_reflection_and_is_read_only && authorize_association_for("attach")
74
+ @reflection.present? && klass.is_a?(::ActiveRecord::Reflection::HasManyReflection) && !has_reflection_and_is_read_only && authorize_association_for(:attach)
73
75
  end
74
76
 
75
77
  def has_reflection_and_is_read_only
@@ -19,8 +19,8 @@
19
19
  } do %>
20
20
  <%= t('avo.detach_item', item: title).capitalize %>
21
21
  <% end %>
22
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
23
22
  <% end %>
23
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
24
24
  <% if can_see_the_edit_button? %>
25
25
  <%= a_link edit_path,
26
26
  color: :primary,
@@ -4,11 +4,13 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
4
4
  include Avo::ResourcesHelper
5
5
  include Avo::ApplicationHelper
6
6
 
7
- def initialize(resource: nil, reflection: nil, parent_model: nil, resource_panel: nil, actions: [])
7
+ def initialize(resource: nil, reflection: nil, parent_resource: nil, parent_model: nil, resource_panel: nil, actions: [])
8
8
  @resource = resource
9
9
  @reflection = reflection
10
10
  @resource_panel = resource_panel
11
11
  @actions = actions
12
+ @parent_model = parent_model
13
+ @parent_resource = parent_resource
12
14
  @view = :show
13
15
  end
14
16
 
@@ -21,6 +21,7 @@ module Avo
21
21
  before_action :set_container_classes
22
22
  before_action :add_initial_breadcrumbs
23
23
  before_action :set_view
24
+ before_action :set_sidebar_open
24
25
 
25
26
  rescue_from Pundit::NotAuthorizedError, with: :render_unauthorized
26
27
  rescue_from ActiveRecord::RecordInvalid, with: :exception_logger
@@ -129,7 +130,11 @@ module Avo
129
130
  end
130
131
 
131
132
  def set_related_model
132
- @related_model = eager_load_files(@related_resource, @related_resource.class.find_scope).find params[:related_id]
133
+ @related_model = if @field.is_a? Avo::Fields::HasOneField
134
+ @model.send params[:related_name]
135
+ else
136
+ eager_load_files(@related_resource, @model.send(params[:related_name])).find params[:related_id]
137
+ end
133
138
  end
134
139
 
135
140
  def set_view
@@ -146,7 +151,7 @@ module Avo
146
151
  is_attach_action = params[model_param_key].blank? && params[:related_name].present? && params[:fields].present?
147
152
 
148
153
  unless is_attach_action
149
- @model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
154
+ @model = @resource.fill_model(@model_to_fill, cast_nullable(model_params), extra_params: extra_params)
150
155
  end
151
156
  end
152
157
 
@@ -155,15 +160,20 @@ module Avo
155
160
  end
156
161
 
157
162
  def hydrate_related_resource
158
- @related_resource.hydrate(view: action_name.to_sym, user: _current_user)
163
+ @related_resource.hydrate(view: action_name.to_sym, user: _current_user, model: @model)
159
164
  end
160
165
 
161
- def authorize_action
162
- if @model.present?
163
- @authorization.set_record(@model).authorize_action action_name.to_sym
164
- else
165
- @authorization.set_record(@resource.model_class).authorize_action action_name.to_sym
166
- end
166
+ def authorize_base_action
167
+ class_to_authorize = @model || @resource.model_class
168
+
169
+ authorize_action class_to_authorize
170
+ end
171
+
172
+ def authorize_action(class_to_authorize, action = nil)
173
+ # Use the provided action or figure it out from the request
174
+ action_to_authorize = action || action_name
175
+
176
+ @authorization.set_record(class_to_authorize).authorize_action action_to_authorize.to_sym
167
177
  end
168
178
 
169
179
  # Get the pluralized resource name for this request
@@ -310,10 +320,15 @@ module Avo
310
320
 
311
321
  def default_url_options
312
322
  if params[:force_locale].present?
313
- { **super, force_locale: params[:force_locale] }
323
+ {**super, force_locale: params[:force_locale]}
314
324
  else
315
325
  super
316
326
  end
317
327
  end
328
+
329
+ def set_sidebar_open
330
+ value = cookies["#{Avo::COOKIES_KEY}.sidebar.open"]
331
+ @sidebar_open = value.blank? || value == "1"
332
+ end
318
333
  end
319
334
  end