avo 2.13.2.pre.2 → 2.13.3.pre.3

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -3
  3. data/app/assets/stylesheets/avo.css +1 -1
  4. data/app/assets/stylesheets/css/fields/progress.css +3 -3
  5. data/app/assets/stylesheets/css/pagination.css +4 -0
  6. data/app/assets/stylesheets/css/search.css +1 -1
  7. data/app/assets/stylesheets/css/sidebar.css +1 -1
  8. data/app/assets/stylesheets/css/tags.css +0 -7
  9. data/app/assets/svgs/arrow-circle-right.svg +1 -1
  10. data/app/assets/svgs/arrow-down.svg +1 -1
  11. data/app/assets/svgs/arrow-up.svg +1 -1
  12. data/app/assets/svgs/bell.svg +1 -1
  13. data/app/assets/svgs/chevron-right.svg +1 -1
  14. data/app/assets/svgs/exclamation.svg +1 -1
  15. data/app/assets/svgs/failed_to_load.svg +5 -4
  16. data/app/assets/svgs/grid-empty-state.svg +11 -10
  17. data/app/assets/svgs/information-circle.svg +1 -1
  18. data/app/assets/svgs/logout.svg +1 -1
  19. data/app/assets/svgs/menu.svg +1 -1
  20. data/app/assets/svgs/play.svg +1 -1
  21. data/app/assets/svgs/switch-vertical.svg +1 -1
  22. data/app/assets/svgs/table-empty-state.svg +12 -11
  23. data/app/assets/svgs/user-circle.svg +1 -1
  24. data/app/assets/svgs/x.svg +1 -1
  25. data/app/components/avo/actions_component.html.erb +2 -2
  26. data/app/components/avo/actions_component.rb +1 -1
  27. data/app/components/avo/button_component.html.erb +1 -0
  28. data/app/components/avo/button_component.rb +3 -3
  29. data/app/components/avo/card_component.html.erb +12 -11
  30. data/app/components/avo/common_field_wrapper_component.html.erb +1 -1
  31. data/app/components/avo/fields/belongs_to_field/edit_component.rb +1 -1
  32. data/app/components/avo/fields/boolean_field/edit_component.html.erb +2 -2
  33. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +1 -1
  34. data/app/components/avo/fields/code_field/edit_component.html.erb +2 -2
  35. data/app/components/avo/fields/country_field/edit_component.html.erb +1 -1
  36. data/app/components/avo/fields/date_field/edit_component.html.erb +2 -2
  37. data/app/components/avo/fields/date_time_field/edit_component.html.erb +2 -2
  38. data/app/components/avo/fields/external_image_field/edit_component.html.erb +1 -1
  39. data/app/components/avo/fields/file_field/edit_component.html.erb +1 -1
  40. data/app/components/avo/fields/files_field/edit_component.html.erb +1 -1
  41. data/app/components/avo/fields/has_one_field/show_component.html.erb +1 -1
  42. data/app/components/avo/fields/markdown_field/edit_component.html.erb +1 -1
  43. data/app/components/avo/fields/markdown_field/show_component.html.erb +1 -1
  44. data/app/components/avo/fields/number_field/edit_component.html.erb +1 -1
  45. data/app/components/avo/fields/password_field/edit_component.html.erb +1 -1
  46. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +1 -1
  47. data/app/components/avo/fields/progress_bar_field/index_component.html.erb +1 -1
  48. data/app/components/avo/fields/progress_bar_field/show_component.html.erb +1 -1
  49. data/app/components/avo/fields/select_field/edit_component.html.erb +1 -1
  50. data/app/components/avo/fields/status_field/edit_component.html.erb +1 -1
  51. data/app/components/avo/fields/tags_field/edit_component.html.erb +3 -3
  52. data/app/components/avo/fields/tags_field/show_component.rb +0 -2
  53. data/app/components/avo/fields/text_field/edit_component.html.erb +1 -1
  54. data/app/components/avo/fields/textarea_field/edit_component.html.erb +1 -1
  55. data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -1
  56. data/app/components/avo/profile_item_component.html.erb +1 -1
  57. data/app/components/avo/resource_component.rb +23 -0
  58. data/app/components/avo/tab_group_component.html.erb +1 -1
  59. data/app/components/avo/tab_switcher_component.rb +2 -2
  60. data/app/components/avo/views/resource_edit_component.html.erb +3 -1
  61. data/app/components/avo/views/resource_edit_component.rb +1 -1
  62. data/app/components/avo/views/resource_index_component.html.erb +2 -2
  63. data/app/components/avo/views/resource_index_component.rb +17 -23
  64. data/app/components/avo/views/resource_show_component.html.erb +7 -4
  65. data/app/components/avo/views/resource_show_component.rb +8 -0
  66. data/app/controllers/avo/base_controller.rb +87 -33
  67. data/app/controllers/avo/search_controller.rb +57 -20
  68. data/app/helpers/avo/actions_helper.rb +4 -0
  69. data/app/helpers/avo/application_helper.rb +36 -44
  70. data/app/helpers/avo/attachments_helper.rb +4 -0
  71. data/app/helpers/avo/resources_helper.rb +2 -2
  72. data/app/javascript/avo.js +1 -0
  73. data/app/javascript/js/controllers/fields/trix_field_controller.js +3 -4
  74. data/app/javascript/js/controllers/item_selector_controller.js +1 -1
  75. data/app/javascript/js/controllers/menu_controller.js +2 -2
  76. data/app/javascript/js/controllers/search_controller.js +42 -15
  77. data/app/views/avo/associations/new.html.erb +3 -2
  78. data/app/views/avo/base/_multiple_select_filter.html.erb +1 -1
  79. data/app/views/avo/base/_text_filter.html.erb +1 -1
  80. data/app/views/avo/partials/_header.html.erb +1 -1
  81. data/app/views/avo/partials/_logo.html.erb +2 -2
  82. data/app/views/avo/partials/_resource_search.html.erb +6 -0
  83. data/app/views/avo/private/_links_and_buttons.html.erb +1 -1
  84. data/app/views/avo/private/design.html.erb +3 -3
  85. data/app/views/kaminari/_first_page.html.erb +3 -0
  86. data/app/views/kaminari/_last_page.html.erb +3 -0
  87. data/app/views/kaminari/_next_page.html.erb +9 -0
  88. data/app/views/kaminari/_page.html.erb +12 -0
  89. data/app/views/kaminari/_prev_page.html.erb +9 -0
  90. data/app/views/layouts/avo/application.html.erb +1 -1
  91. data/avo.gemspec +1 -0
  92. data/lib/avo/concerns/has_editable_controls.rb +1 -1
  93. data/lib/avo/configuration.rb +0 -5
  94. data/lib/avo/dashboards/chartkick_card.rb +1 -1
  95. data/lib/avo/engine.rb +3 -0
  96. data/lib/avo/fields/base_field.rb +2 -1
  97. data/lib/avo/fields/concerns/is_readonly.rb +17 -0
  98. data/lib/avo/fields/has_base_field.rb +2 -0
  99. data/lib/avo/hosts/search_scope_host.rb +7 -0
  100. data/lib/avo/licensing/pro_license.rb +0 -1
  101. data/lib/avo/version.rb +1 -1
  102. data/lib/generators/avo/templates/field/components/edit_component.html.erb.tt +1 -1
  103. data/lib/generators/avo/templates/initializer/avo.tt +1 -15
  104. data/lib/generators/avo/templates/resource/resource.tt +1 -1
  105. data/public/avo-assets/avo.css +248 -286
  106. data/public/avo-assets/avo.js +47 -47
  107. data/public/avo-assets/avo.js.map +2 -2
  108. metadata +25 -3
  109. data/app/views/avo/partials/_branding.html.erb +0 -5
  110. data/lib/avo/configuration/branding.rb +0 -65
@@ -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.readonly,
5
+ disabled: @field.is_readonly?,
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.readonly,
10
+ disabled: @field.is_readonly?,
11
11
  max: @field.max,
12
12
  min: 0,
13
13
  placeholder: @field.placeholder,
@@ -4,5 +4,5 @@
4
4
  <%= @field.value %><%= @field.value_suffix if @field.value_suffix.present? %>
5
5
  </div>
6
6
  <% end %>
7
- <progress min="0" max="<%= @field.max %>" value="<%= @field.value %>" class="block w-auto"></progress>
7
+ <progress min="0" max="<%= @field.max %>" value="<%= @field.value %>" class="block w-24"></progress>
8
8
  <% end %>
@@ -4,5 +4,5 @@
4
4
  <%= @field.value %><%= @field.value_suffix if @field.value_suffix.present? %>
5
5
  </div>
6
6
  <% end %>
7
- <progress min="0" max="<%= @field.max %>" value="<%= @field.value %>" class="block h-3 w-full"></progress>
7
+ <progress min="0" max="<%= @field.max %>" value="<%= @field.value %>" class="block w-full"></progress>
8
8
  <% end %>
@@ -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.readonly,
11
+ disabled: @field.is_readonly?,
12
12
  style: @field.get_html(:style, view: view, element: :input),
13
13
  value: @field.model.present? ? @field.model[@field.id] : @field.value,
14
14
  placeholder: @field.include_blank.present? ? nil : @field.placeholder
@@ -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.readonly,
5
+ disabled: @field.is_readonly?,
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
@@ -10,14 +10,14 @@
10
10
  data: {
11
11
  'tags-field-target': 'fakeInput',
12
12
  },
13
- disabled: @field.readonly,
13
+ disabled: @field.is_readonly?,
14
14
  placeholder: @field.placeholder,
15
15
  style: @field.get_html(:style, view: view, element: :input),
16
16
  value: ''
17
17
  %>
18
18
  <%# real field %>
19
19
  <%= @form.text_field @field.id,
20
- class: classes("hidden w-full border-primary-500 focus-within:border-primary-500"),
20
+ class: classes("hidden w-full"),
21
21
  data: {
22
22
  'tags-field-target': 'input',
23
23
  'whitelist-items': @field.suggestions.to_json,
@@ -26,7 +26,7 @@
26
26
  'delimiters': @field.delimiters,
27
27
  'close-on-select': @field.close_on_select ? 1 : 0,
28
28
  },
29
- disabled: @field.readonly,
29
+ disabled: @field.is_readonly?,
30
30
  placeholder: @field.placeholder,
31
31
  style: @field.get_html(:style, view: view, element: :input),
32
32
  value: @field.field_value.to_json
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # require_relative 'item_labels'
4
-
5
3
  class Avo::Fields::TagsField::ShowComponent < Avo::Fields::ShowComponent
6
4
  include Avo::Fields::Concerns::ItemLabels
7
5
  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.readonly,
5
+ disabled: @field.is_readonly?,
6
6
  placeholder: @field.placeholder,
7
7
  style: @field.get_html(:style, view: view, element: :input)
8
8
  %>
@@ -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.readonly,
5
+ disabled: @field.is_readonly?,
6
6
  placeholder: @field.placeholder,
7
7
  rows: @field.rows,
8
8
  style: @field.get_html(:style, view: view, element: :input)
@@ -24,7 +24,7 @@
24
24
  <%= @form.text_area @field.id,
25
25
  class: classes("w-full hidden"),
26
26
  data: @field.get_html(:data, view: view, element: :input),
27
- disabled: @field.readonly,
27
+ disabled: @field.is_readonly?,
28
28
  id: trix_id,
29
29
  placeholder: @field.placeholder,
30
30
  style: @field.get_html(:style, view: view, element: :input)
@@ -1,3 +1,3 @@
1
- <%= link_to path, class: "flex-1 flex items-center justify-center bg-white text-left cursor-pointer text-gray-800 font-semibold hover:bg-primary-100 block px-4 py-1 w-full py-3 text-center rounded w-full", target: target, title: title do %>
1
+ <%= link_to path, class: "flex-1 flex items-center justify-center bg-white text-left cursor-pointer text-gray-800 font-semibold hover:bg-blue-100 block px-4 py-1 w-full py-3 text-center rounded w-full", target: target, title: title do %>
2
2
  <%= helpers.svg(icon, class: 'h-4 mr-1') if icon.present? %> <%= label %>
3
3
  <% end %>
@@ -36,6 +36,14 @@ class Avo::ResourceComponent < Avo::BaseComponent
36
36
  @resource.authorization.authorize_action(:destroy, raise_exception: false)
37
37
  end
38
38
 
39
+ def can_see_the_actions_button?
40
+ return false if @actions.blank?
41
+
42
+ return authorize_association_for(:act_on) if @reflection.present?
43
+
44
+ @resource.authorization.authorize_action(:act_on, raise_exception: false) && !has_reflection_and_is_read_only
45
+ end
46
+
39
47
  def destroy_path
40
48
  helpers.resource_path(model: @resource.model, resource: @resource)
41
49
  end
@@ -72,6 +80,21 @@ class Avo::ResourceComponent < Avo::BaseComponent
72
80
  end
73
81
  end
74
82
 
83
+ def has_reflection_and_is_read_only
84
+ if @reflection.present? && @reflection.active_record.name && @reflection.name
85
+ fields = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name).get_field_definitions
86
+ filtered_fields = fields.filter { |f| f.id == @reflection.name }
87
+ else
88
+ return false
89
+ end
90
+
91
+ if filtered_fields.present?
92
+ filtered_fields.find { |f| f.id == @reflection.name }.readonly
93
+ else
94
+ false
95
+ end
96
+ end
97
+
75
98
  private
76
99
 
77
100
  def via_resource?
@@ -25,7 +25,7 @@
25
25
  # On edit screens we want to load each tab because we wnst the DOM to have the fields present on form submission.
26
26
  # If you have a field which is in the second tab and it's required, the form submission will fail because the required field is not in view, and we don't want that.
27
27
  # We also want to load the current tab
28
- should_lazy_load = if @view.to_s.in?(['edit', 'new'])
28
+ should_lazy_load = if @view.to_s.in?(['edit', 'new', 'update', 'create'])
29
29
  false
30
30
  else
31
31
  !is_current_tab
@@ -30,11 +30,11 @@ class Avo::TabSwitcherComponent < Avo::BaseComponent
30
30
  end
31
31
 
32
32
  def is_edit?
33
- @view == :edit
33
+ @view.in?([:edit, :update])
34
34
  end
35
35
 
36
36
  def is_new?
37
- @view == :new
37
+ @view.in?([:new, :create])
38
38
  end
39
39
 
40
40
  def is_initial_load?
@@ -22,7 +22,9 @@
22
22
  icon: 'arrow-left' do %>
23
23
  <%= t('avo.cancel').capitalize %>
24
24
  <% end %>
25
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
25
+ <% if can_see_the_actions_button? %>
26
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
27
+ <% end %>
26
28
  <% if can_see_the_save_button? %>
27
29
  <%= a_button color: :primary,
28
30
  style: :primary,
@@ -42,7 +42,7 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
42
42
  end
43
43
 
44
44
  def is_edit?
45
- view == :edit
45
+ view.in?([:edit, :update])
46
46
  end
47
47
 
48
48
  def form_method
@@ -32,9 +32,9 @@
32
32
  data-selected-resources-name="<%= @resource.model_key %>"
33
33
  data-selected-resources="[]"
34
34
  >
35
- <% if @resource.search_query.present? %>
35
+ <% unless hide_search_input %>
36
36
  <div class="flex items-center px-4 w-64">
37
- <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.route_key} %>
37
+ <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.route_key, via_reflection: via_reflection} %>
38
38
  </div>
39
39
  <% else %>
40
40
  <%# Offset for the space-y-2 property when the search is missing %>
@@ -59,14 +59,6 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
59
59
  @resource.authorization.authorize_action(:new, raise_exception: false) && !has_reflection_and_is_read_only
60
60
  end
61
61
 
62
- def can_see_the_actions_button?
63
- return false if @actions.blank?
64
-
65
- return authorize_association_for(:act_on) if @reflection.present?
66
-
67
- @resource.authorization.authorize_action(:act_on, raise_exception: false) && !has_reflection_and_is_read_only
68
- end
69
-
70
62
  def can_attach?
71
63
  klass = @reflection
72
64
  klass = @reflection.through_reflection if klass.is_a? ::ActiveRecord::Reflection::ThroughReflection
@@ -74,21 +66,6 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
74
66
  @reflection.present? && klass.is_a?(::ActiveRecord::Reflection::HasManyReflection) && !has_reflection_and_is_read_only && authorize_association_for(:attach)
75
67
  end
76
68
 
77
- def has_reflection_and_is_read_only
78
- if @reflection.present? && @reflection.active_record.name && @reflection.name
79
- fields = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name).get_field_definitions
80
- filtered_fields = fields.filter { |f| f.id == @reflection.name }
81
- else
82
- return false
83
- end
84
-
85
- if filtered_fields.present?
86
- filtered_fields.find { |f| f.id == @reflection.name }.readonly
87
- else
88
- false
89
- end
90
- end
91
-
92
69
  def create_path
93
70
  args = {}
94
71
 
@@ -143,6 +120,12 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
143
120
  @resource.resource_description
144
121
  end
145
122
 
123
+ def hide_search_input
124
+ return true unless @resource.search_query.present?
125
+
126
+ field&.hide_search_input || false
127
+ end
128
+
146
129
  private
147
130
 
148
131
  def reflection_model_class
@@ -153,4 +136,15 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
153
136
  field.custom_name? ? field.name : field.plural_name
154
137
  end
155
138
 
139
+ def via_reflection
140
+ return unless @reflection.present?
141
+
142
+ {
143
+ association: 'has_many',
144
+ association_id: @reflection.name,
145
+ class: reflection_model_class,
146
+ id: @parent_model.id
147
+ }
148
+ end
149
+
156
150
  end
@@ -36,9 +36,10 @@
36
36
  tippy: control.title ? :tooltip : nil,
37
37
  'resource-id': @resource.model.id,
38
38
  } do %>
39
+ <%= control.label %>
39
40
  <% end %>
40
41
  <% end %>
41
- <% elsif control.actions_list? %>
42
+ <% elsif can_see_the_actions_button? && control.actions_list? %>
42
43
  <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view, exclude: control.exclude, style: control.style, color: control.color %>
43
44
  <% elsif control.edit_button? %>
44
45
  <% if @resource.authorization.authorize_action(:edit, raise_exception: false) %>
@@ -90,7 +91,7 @@
90
91
  data: {
91
92
  confirm: "Are you sure you want to detach this #{title}."
92
93
  } do %>
93
- <%= t('avo.detach_item', item: title).capitalize %>
94
+ <%= control.label %>
94
95
  <% end %>
95
96
  <% end %>
96
97
  <% end %>
@@ -125,7 +126,7 @@
125
126
  <%= t('avo.go_back') %>
126
127
  <% end %>
127
128
  <% if can_see_the_destroy_button? %>
128
- <%= a_button url: helpers.resource_path(model: @resource.model, resource: @resource),
129
+ <%= a_button url: destroy_path,
129
130
  method: :delete,
130
131
  local: true,
131
132
  style: :text,
@@ -141,7 +142,9 @@
141
142
  <%= t('avo.delete').capitalize %>
142
143
  <% end %>
143
144
  <% end %>
144
- <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
145
+ <% if can_see_the_actions_button? %>
146
+ <%= render Avo::ActionsComponent.new actions: @actions, resource: @resource, view: @view %>
147
+ <% end %>
145
148
  <% if @resource.authorization.authorize_action(:edit, raise_exception: false) %>
146
149
  <%= a_link edit_path,
147
150
  color: :primary,
@@ -45,6 +45,14 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
45
45
  helpers.edit_resource_path(model: @resource.model, resource: @resource, **args)
46
46
  end
47
47
 
48
+ def destroy_path
49
+ if params[:via_resource_class].present?
50
+ helpers.resource_path(model: @resource.model, resource: @resource, referrer: back_path)
51
+ else
52
+ helpers.resource_path(model: @resource.model, resource: @resource)
53
+ end
54
+ end
55
+
48
56
  private
49
57
 
50
58
  # In development and test environments we shoudl show the invalid field errors
@@ -115,7 +115,7 @@ module Avo
115
115
  add_breadcrumb via_resource.model_title, resource_path(model: via_model, resource: via_resource)
116
116
  end
117
117
 
118
- add_breadcrumb @resource.plural_name.humanize
118
+ add_breadcrumb @resource.plural_name.humanize, resources_path(resource: @resource)
119
119
  add_breadcrumb t("avo.new").humanize
120
120
  end
121
121
 
@@ -129,21 +129,14 @@ module Avo
129
129
  @reflection = @model._reflections[params[:via_relation]]
130
130
  # Figure out what kind of association does the record have with the parent record
131
131
 
132
- # belongs_to
133
- # has_many
132
+ # Fills in the required infor for belongs_to and has_many
134
133
  # Get the foreign key and set it to the id we received in the params
135
134
  if @reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection) || @reflection.is_a?(ActiveRecord::Reflection::HasManyReflection)
136
- foreign_key = @reflection.foreign_key
137
- @model.send("#{foreign_key}=", params[:via_resource_id])
135
+ @model.send("#{@reflection.foreign_key}=", params[:via_resource_id])
138
136
  @model.save
139
137
  end
140
138
 
141
- # has_one
142
- # has_one_through
143
-
144
- # has_many_through
145
- # has_and_belongs_to_many
146
- # polymorphic
139
+ # For when working with has_one, has_one_through, has_many_through, has_and_belongs_to_many, polymorphic
147
140
  if @reflection.is_a? ActiveRecord::Reflection::ThroughReflection
148
141
  # find the record
149
142
  via_resource = ::Avo::App.get_resource_by_model_name params[:via_relation_class]
@@ -153,13 +146,14 @@ module Avo
153
146
  end
154
147
  end
155
148
 
156
- respond_to do |format|
157
- if saved
158
- format.html { redirect_to after_create_path, notice: "#{@resource.name} #{t("avo.was_successfully_created")}." }
159
- else
160
- flash.now[:error] = t "avo.you_missed_something_check_form"
161
- format.html { render :new, status: :unprocessable_entity }
162
- end
149
+ add_breadcrumb @resource.plural_name.humanize, resources_path(resource: @resource)
150
+ add_breadcrumb t("avo.new").humanize
151
+ set_actions
152
+
153
+ if saved
154
+ create_success_action
155
+ else
156
+ create_fail_action
163
157
  end
164
158
  end
165
159
 
@@ -171,26 +165,20 @@ module Avo
171
165
  # model gets instantiated and filled in the fill_model method
172
166
  saved = save_model
173
167
  @resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
168
+ set_actions
174
169
 
175
- respond_to do |format|
176
- if saved
177
- format.html { redirect_to after_update_path, notice: "#{@resource.name} #{t("avo.was_successfully_updated")}." }
178
- else
179
- flash.now[:error] = t "avo.you_missed_something_check_form"
180
- format.html { render :edit, status: :unprocessable_entity }
181
- end
170
+ if saved
171
+ update_success_action
172
+ else
173
+ update_fail_action
182
174
  end
183
175
  end
184
176
 
185
177
  def destroy
186
- respond_to do |format|
187
- if destroy_model
188
- format.html { redirect_to params[:referrer] || resources_path(resource: @resource, turbo_frame: params[:turbo_frame], view_type: params[:view_type]), notice: t("avo.resource_destroyed", attachment_class: @attachment_class) }
189
- else
190
- error_message = @errors.present? ? @errors.first : t("avo.failed")
191
-
192
- format.html { redirect_back fallback_location: params[:referrer] || resources_path(resource: @resource, turbo_frame: params[:turbo_frame], view_type: params[:view_type]), error: error_message }
193
- end
178
+ if destroy_model
179
+ destroy_success_action
180
+ else
181
+ destroy_fail_action
194
182
  end
195
183
  end
196
184
 
@@ -401,6 +389,27 @@ module Avo
401
389
  add_breadcrumb t("avo.edit").humanize
402
390
  end
403
391
 
392
+ def create_success_action
393
+ respond_to do |format|
394
+ format.html { redirect_to after_create_path, notice: create_success_message}
395
+ end
396
+ end
397
+
398
+ def create_fail_action
399
+ respond_to do |format|
400
+ flash.now[:error] = create_fail_message
401
+ format.html { render :new, status: :unprocessable_entity }
402
+ end
403
+ end
404
+
405
+ def create_success_message
406
+ "#{@resource.name} #{t("avo.was_successfully_created")}."
407
+ end
408
+
409
+ def create_fail_message
410
+ t "avo.you_missed_something_check_form"
411
+ end
412
+
404
413
  def after_create_path
405
414
  # If this is an associated record return to the association show page
406
415
  if params[:via_relation_class].present? && params[:via_resource_id].present?
@@ -412,12 +421,57 @@ module Avo
412
421
  redirect_path_from_resource_option(:after_create_path) || resource_path(model: @model, resource: @resource)
413
422
  end
414
423
 
424
+ def update_success_action
425
+ respond_to do |format|
426
+ format.html { redirect_to after_update_path, notice: update_success_message }
427
+ end
428
+ end
429
+
430
+ def update_fail_action
431
+ respond_to do |format|
432
+ flash.now[:error] = update_fail_message
433
+ format.html { render :edit, status: :unprocessable_entity }
434
+ end
435
+ end
436
+
437
+ def update_success_message
438
+ "#{@resource.name} #{t("avo.was_successfully_updated")}."
439
+ end
440
+
441
+ def update_fail_message
442
+ t "avo.you_missed_something_check_form"
443
+ end
444
+
415
445
  def after_update_path
416
446
  return params[:referrer] if params[:referrer].present?
417
447
 
418
448
  redirect_path_from_resource_option(:after_update_path) || resource_path(model: @model, resource: @resource)
419
449
  end
420
450
 
451
+ def destroy_success_action
452
+ respond_to do |format|
453
+ format.html { redirect_to after_destroy_path, notice: destroy_success_message }
454
+ end
455
+ end
456
+
457
+ def destroy_fail_action
458
+ respond_to do |format|
459
+ format.html { redirect_back fallback_location: params[:referrer] || resources_path(resource: @resource, turbo_frame: params[:turbo_frame], view_type: params[:view_type]), error: destroy_fail_message }
460
+ end
461
+ end
462
+
463
+ def destroy_success_message
464
+ t("avo.resource_destroyed", attachment_class: @attachment_class)
465
+ end
466
+
467
+ def destroy_fail_message
468
+ @errors.present? ? @errors.first : t("avo.failed")
469
+ end
470
+
471
+ def after_destroy_path
472
+ params[:referrer] || resources_path(resource: @resource, turbo_frame: params[:turbo_frame], view_type: params[:view_type])
473
+ end
474
+
421
475
  def redirect_path_from_resource_option(action = :after_update_path)
422
476
  return nil if @resource.class.send(action).blank?
423
477
 
@@ -44,10 +44,13 @@ module Avo
44
44
  end
45
45
 
46
46
  def search_resource(resource)
47
- query = resource.search_query.call(params: params).limit(8)
47
+ query = Avo::Hosts::SearchScopeHost.new(
48
+ block: resource.search_query,
49
+ params: params,
50
+ scope: resource.class.scope.limit(8)
51
+ ).handle
48
52
 
49
- # Figure out if this is a belongs_to search
50
- query = apply_attach_scope query
53
+ query = apply_scope(query) if should_apply_any_scope?
51
54
 
52
55
  results = apply_search_metadata(query, resource)
53
56
 
@@ -67,22 +70,19 @@ module Avo
67
70
  [resource.name.pluralize.downcase, result_object]
68
71
  end
69
72
 
70
- # Figure out if it's a belongs to search and if it has the attach_scope block present.
71
- # If so, set the parent for those edit view and the parent with the grandparent for the new view.
72
- def apply_attach_scope(query)
73
- return query if params[:via_reflection_class].blank?
74
-
75
- # Fetch the field
76
- field = belongs_to_field
77
-
78
- # No need to modify the query if there's no attach_scope present.
79
- return query if field.attach_scope.blank?
80
-
81
- # Try to fetch the parent.
82
- if params[:via_reflection_id].present?
83
- parent = params[:via_reflection_class].safe_constantize.find params[:via_reflection_id]
73
+ # When searching in a `has_many` association and will scope out the records against the parent record.
74
+ # This is also used when looking for `belongs_to` associations, and this method applies the parents `attach_scope` if present.
75
+ def apply_scope(query)
76
+ if should_apply_has_many_scope?
77
+ apply_has_many_scope
78
+ elsif should_apply_attach_scope?
79
+ apply_attach_scope(query, parent)
84
80
  end
81
+ end
85
82
 
83
+ # Parent passed as argument to be used as a variable instead of the method "def parent"
84
+ # Otherwise parent = params...safe_constantize... will try to call method "def parent="
85
+ def apply_attach_scope(query, parent)
86
86
  # If the parent is nil it probably means that someone's creating the record so it's not attached yet.
87
87
  # In these scenarios, try to find the grandparent for the new views where the parent is nil
88
88
  # and initialize the parent record with the grandparent attached so the user has the required information
@@ -94,8 +94,13 @@ module Avo
94
94
  )
95
95
  end
96
96
 
97
- # Add to the query
98
- Avo::Hosts::AssociationScopeHost.new(block: belongs_to_field.attach_scope, query: query, parent: parent).handle
97
+ Avo::Hosts::AssociationScopeHost.new(block: attach_scope, query: query, parent: parent).handle
98
+ end
99
+
100
+ def apply_has_many_scope
101
+ scope = parent.send(params[:via_association_id])
102
+
103
+ Avo::Hosts::SearchScopeHost.new(block: @resource.search_query, params: params, scope: scope).handle
99
104
  end
100
105
 
101
106
  def apply_search_metadata(models, avo_resource)
@@ -118,9 +123,41 @@ module Avo
118
123
  end
119
124
  end
120
125
 
121
- def belongs_to_field
126
+ private
127
+
128
+ def should_apply_has_many_scope?
129
+ params[:via_association] == "has_many" && @resource.search_query.present?
130
+ end
131
+
132
+ def should_apply_attach_scope?
133
+ params[:via_association] == "belongs_to" && attach_scope.present?
134
+ end
135
+
136
+ def should_apply_any_scope?
137
+ should_apply_has_many_scope? || should_apply_attach_scope?
138
+ end
139
+
140
+ def attach_scope
141
+ @attach_scope ||= field&.attach_scope
142
+ end
143
+
144
+ def field
145
+ @field ||= fetch_field
146
+ end
147
+
148
+ def parent
149
+ @parent ||= fetch_parent
150
+ end
151
+
152
+ def fetch_field
122
153
  fields = ::Avo::App.get_resource_by_model_name(params[:via_reflection_class]).get_field_definitions
123
154
  fields.find { |f| f.id.to_s == params[:via_association_id] }
124
155
  end
156
+
157
+ def fetch_parent
158
+ return unless params[:via_reflection_id].present?
159
+
160
+ params[:via_reflection_class].safe_constantize.find params[:via_reflection_id]
161
+ end
125
162
  end
126
163
  end
@@ -0,0 +1,4 @@
1
+ module Avo
2
+ module ActionsHelper
3
+ end
4
+ end