avo 1.20.2.pre.2 → 1.21.0.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -3
  3. data/Gemfile.lock +3 -5
  4. data/app/assets/builds/avo.css +0 -16
  5. data/app/assets/builds/avo.js +234 -317
  6. data/app/assets/builds/avo.js.map +2 -2
  7. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +33 -39
  8. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  9. data/app/controllers/avo/application_controller.rb +13 -11
  10. data/app/controllers/avo/base_controller.rb +4 -1
  11. data/app/controllers/avo/relations_controller.rb +4 -4
  12. data/app/controllers/avo/search_controller.rb +1 -0
  13. data/app/javascript/js/controllers/fields/belongs_to_field_controller.js +33 -96
  14. data/app/javascript/js/controllers/search_controller.js +17 -83
  15. data/app/views/avo/partials/_global_search.html.erb +1 -0
  16. data/app/views/avo/partials/_javascript.html.erb +0 -1
  17. data/app/views/avo/partials/_resource_search.html.erb +1 -0
  18. data/db/factories.rb +0 -4
  19. data/lib/avo/fields/base_field.rb +1 -1
  20. data/lib/avo/fields/belongs_to_field.rb +2 -19
  21. data/lib/avo/fields/files_field.rb +1 -2
  22. data/lib/avo/licensing/pro_license.rb +1 -2
  23. data/lib/avo/version.rb +1 -1
  24. data/lib/generators/avo/templates/locales/avo.en.yml +0 -1
  25. data/public/avo-assets/avo.css +0 -16
  26. data/public/avo-assets/avo.js +234 -317
  27. data/public/avo-assets/avo.js.map +2 -2
  28. metadata +2 -5
  29. data/app/assets/svgs/x.svg +0 -3
  30. data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +0 -22
  31. data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +0 -33
@@ -1,34 +1,22 @@
1
- <%
2
- if @field.types.present? # It's a polymorphic association
3
-
4
- # Set the model keys so we can pass them over
5
- model_keys = @field.types.map do |type|
6
- resource = Avo::App.get_resource_by_model_name(type.to_s)
7
- [type.to_s, resource.model_key]
8
- end.to_h
9
- %>
10
- <div data-controller="belongs-to-field"
11
- data-searchable="<%= @field.searchable %>"
12
- data-association="<%= @field.id %>"
13
- data-association-class="<%= @field&.target_resource&.model_class || nil %>"
14
- >
1
+ <% if @field.types.present? %>
2
+ <div data-controller="belongs-to-field">
15
3
  <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
16
4
  <%= @form.select "#{@field.foreign_key}_type", @field.types.map { |type| [type.to_s.underscore.humanize, type.to_s] },
17
5
  {
18
- value: @field.value,
19
6
  include_blank: @field.placeholder,
20
7
  },
21
8
  {
22
9
  class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
23
10
  disabled: disabled,
24
11
  'data-belongs-to-field-target': "select",
25
- 'data-action': 'change->belongs-to-field#changeType'
12
+ 'data-action': 'change->belongs-to-field#changedType'
26
13
  }
27
14
  %>
28
15
  <%
29
16
  # If the select field is disabled, no value will be sent. It's how HTML works.
30
17
  # Thus the extra hidden field to actually send the related id to the server.
31
- if disabled %>
18
+ if disabled
19
+ %>
32
20
  <%= @form.hidden_field "#{@field.foreign_key}_type" %>
33
21
  <% end %>
34
22
  <% end %>
@@ -38,18 +26,21 @@
38
26
  data-type="<%= type %>"
39
27
  >
40
28
  <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, label: type.to_s.underscore.humanize do %>
41
- <% if @field.searchable %>
42
- <%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form, field: @field, type: type, model_key: model_keys[type.to_s], foreign_key: "#{@field.foreign_key}_id" %>
43
- <% else %>
44
- <%= @form.select "#{@field.foreign_key}_id", options_for_select(@field.values_for_type(type), @field.value&.class == type ? @field.field_value : nil),
45
- {
46
- include_blank: @field.placeholder,
47
- },
48
- {
49
- class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
50
- disabled: disabled
51
- }
29
+ <%= @form.select "#{@field.foreign_key}_id", @field.values_for_type(type),
30
+ {
31
+ include_blank: @field.placeholder,
32
+ },
33
+ {
34
+ class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
35
+ disabled: disabled
36
+ }
52
37
  %>
38
+ <%
39
+ # If the select field is disabled, no value will be sent. It's how HTML works.
40
+ # Thus the extra hidden field to actually send the related id to the server.
41
+ if disabled
42
+ %>
43
+ <%= @form.hidden_field "#{@field.foreign_key}_id" %>
53
44
  <% end %>
54
45
  <% end %>
55
46
  </div>
@@ -57,18 +48,21 @@
57
48
  </div>
58
49
  <% else %>
59
50
  <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
60
- <% if @field.searchable %>
61
- <%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form, field: @field, model_key: @field.target_resource&.model_key, foreign_key: @field.foreign_key %>
62
- <% else %>
63
- <%= @form.select @field.foreign_key, @field.options.map { |o| [o[:label], o[:value]] },
64
- {
65
- include_blank: @field.placeholder,
66
- },
67
- {
68
- class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
69
- disabled: disabled
70
- }
51
+ <%= @form.select @field.foreign_key, @field.options.map { |o| [o[:label], o[:value]] },
52
+ {
53
+ include_blank: @field.placeholder,
54
+ },
55
+ {
56
+ class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
57
+ disabled: disabled
58
+ }
71
59
  %>
60
+ <%
61
+ # If the select field is disabled, no value will be sent. It's how HTML works.
62
+ # Thus the extra hidden field to actually send the related id to the server.
63
+ if disabled
64
+ %>
65
+ <%= @form.hidden_field @field.foreign_key %>
72
66
  <% end %>
73
67
  <% end %>
74
68
  <% end %>
@@ -24,7 +24,7 @@
24
24
  data-selected-resources="[]"
25
25
  >
26
26
  <div class="flex items-center px-6 w-64">
27
- <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.model_key} if @resource.search_query.present? %>
27
+ <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.model_class.model_name.plural} if @resource.search_query.present? %>
28
28
  </div>
29
29
  <div class="flex justify-end items-center px-6 space-x-3">
30
30
  <%= 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? %>
@@ -111,13 +111,7 @@ module Avo
111
111
  end
112
112
 
113
113
  def fill_model
114
- # We have to skip filling the the model if this is an attach action
115
- is_attach_action = params[model_param_key].blank? && params[:related_name].present? && params[:fields].present?
116
- # puts ['fill_model->', is_attach_action, model_param_key].inspect
117
-
118
- unless is_attach_action
119
- @model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
120
- end
114
+ @model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
121
115
  end
122
116
 
123
117
  def hydrate_resource
@@ -193,6 +187,18 @@ module Avo
193
187
  query
194
188
  end
195
189
 
190
+ # def authorize_user
191
+ # return if params[:controller] == 'avo/search'
192
+
193
+ # model = record = resource.model
194
+
195
+ # if ['show', 'edit', 'update'].include?(params[:action]) && params[:controller] == 'avo/resources'
196
+ # record = resource
197
+ # end
198
+
199
+ # # AuthorizationService::authorize_action _current_user, record, params[:action] return render_unauthorized unless
200
+ # end
201
+
196
202
  def _authenticate!
197
203
  instance_eval(&Avo.configuration.authenticate)
198
204
  end
@@ -237,9 +243,5 @@ module Avo
237
243
  def on_api_path
238
244
  request.original_url.match?(/.*#{Avo::App.root_path}\/avo_api\/.*/)
239
245
  end
240
-
241
- def model_param_key
242
- @resource.form_scope
243
- end
244
246
  end
245
247
  end
@@ -7,7 +7,6 @@ module Avo
7
7
  before_action :hydrate_resource
8
8
  before_action :set_model, only: [:show, :edit, :destroy, :update]
9
9
  before_action :set_model_to_fill
10
- before_action :fill_model, only: [:create, :update]
11
10
  before_action :authorize_action
12
11
  before_action :reset_pagination_if_filters_changed, only: :index
13
12
  before_action :cache_applied_filters, only: :index
@@ -113,6 +112,7 @@ module Avo
113
112
 
114
113
  def create
115
114
  # model gets instantiated and filled in the fill_model method
115
+ fill_model
116
116
  saved = @model.save
117
117
  @resource.hydrate(model: @model, view: :new, user: _current_user)
118
118
 
@@ -166,6 +166,7 @@ module Avo
166
166
 
167
167
  def update
168
168
  # model gets instantiated and filled in the fill_model method
169
+ fill_model
169
170
  saved = @model.save
170
171
  @resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
171
172
 
@@ -193,6 +194,8 @@ module Avo
193
194
  private
194
195
 
195
196
  def model_params
197
+ model_param_key = @resource.form_scope
198
+
196
199
  request_params = params.require(model_param_key).permit(permitted_params)
197
200
 
198
201
  if @resource.devise_password_optional && request_params[:password].blank? && request_params[:password_confirmation].blank?
@@ -4,11 +4,11 @@ module Avo
4
4
  class RelationsController < BaseController
5
5
  before_action :set_model, only: [:show, :index, :new, :create, :destroy]
6
6
  before_action :set_related_resource_name
7
- before_action :set_related_resource, only: [:show, :index, :new, :create, :destroy]
8
- before_action :hydrate_related_resource, only: [:show, :index, :new, :create, :destroy]
7
+ before_action :set_related_resource
8
+ before_action :hydrate_related_resource
9
9
  before_action :set_related_model, only: [:show]
10
- before_action :set_attachment_class, only: [:show, :index, :new, :create, :destroy]
11
- before_action :set_attachment_resource, only: [:show, :index, :new, :create, :destroy]
10
+ before_action :set_attachment_class
11
+ before_action :set_attachment_resource
12
12
  before_action :set_attachment_model, only: [:create, :destroy]
13
13
  before_action :set_reflection, only: [:index, :show]
14
14
 
@@ -60,6 +60,7 @@ module Avo
60
60
  _id: model.id,
61
61
  _label: resource.label,
62
62
  _url: resource.record_path,
63
+ model: model
63
64
  }
64
65
 
65
66
  if App.license.has_with_trial(:enhanced_search_results)
@@ -1,128 +1,65 @@
1
1
  import { Controller } from 'stimulus'
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ['select', 'type', 'loadAssociationLink'];
5
-
6
- defaults = {};
4
+ static targets = ['select', 'type']
7
5
 
8
6
  get selectedType() {
9
7
  return this.selectTarget.value
10
8
  }
11
9
 
12
- get isSearchable() {
13
- return this.context.scope.element.dataset.searchable === 'true'
14
- }
15
-
16
- get association() {
17
- return this.context.scope.element.dataset.association
10
+ connect() {
11
+ this.setValidNames()
12
+ this.changedType()
18
13
  }
19
14
 
20
- get associationClass() {
21
- return this.context.scope.element.dataset.associationClass
22
- }
15
+ setValidNames() {
16
+ this.typeTargets.forEach((target) => {
17
+ const { type } = target.dataset
18
+ const select = target.querySelector('select')
19
+ const name = select.getAttribute('name')
23
20
 
24
- connect() {
25
- this.copyValidNames()
26
- this.changeType() // Do the initial type change
21
+ select.setAttribute('valid-name', name)
22
+ if (this.selectedType !== type) {
23
+ select.selectedIndex = 0
24
+ }
25
+ })
27
26
  }
28
27
 
29
- changeType() {
30
- this.hideAllTypes()
31
- this.showType(this.selectTarget.value)
28
+ changedType() {
29
+ this.hideAllTypeTargets()
30
+ this.enableType(this.selectTarget.value)
32
31
  }
33
32
 
34
- // Private
35
-
36
- hideAllTypes() {
33
+ hideAllTypeTargets() {
37
34
  this.typeTargets.forEach((target) => {
38
- target.classList.add('hidden')
39
-
35
+ this.hideTarget(target)
40
36
  this.invalidateTarget(target)
41
37
  })
42
38
  }
43
39
 
44
- /**
45
- * Used for invalidating select fields when switching between types so they don't automatically override the previous id.
46
- * Ex: There are two types Article and Project and the Comment has commentable_id 3 and commentable_type: Article
47
- * When you change the type from Project to Article the Project field will override the commentable_id value
48
- * because it was rendered later (alphabetical sorting) and the browser will pick that one up.
49
- * So we go and copy the name attribute to valid-name for all types and then copy it back to name when the user selects it.
50
- */
51
-
52
- /**
53
- * This method does the initial copying from name to valid-name.
54
- */
55
- copyValidNames() {
56
- this.typeTargets.forEach((target) => {
57
- const { type } = target.dataset
58
-
59
- if (this.isSearchable) {
60
- const textInput = target.querySelector('input[type="text"]')
61
- if (textInput) {
62
- textInput.setAttribute('valid-name', textInput.getAttribute('name'))
63
- }
64
-
65
- const hiddenInput = target.querySelector('input[type="hidden"]')
66
- if (hiddenInput) {
67
- hiddenInput.setAttribute(
68
- 'valid-name',
69
- hiddenInput.getAttribute('name'),
70
- )
71
- }
72
- } else {
73
- const select = target.querySelector('select')
74
- if (select) {
75
- select.setAttribute('valid-name', select.getAttribute('name'))
76
- }
77
-
78
- if (this.selectedType !== type) {
79
- select.selectedIndex = 0
80
- }
81
- }
82
- })
40
+ hideTarget(target) {
41
+ target.classList.add('hidden')
83
42
  }
84
43
 
85
- showType(type) {
86
- const target = this.typeTargets.find(
87
- (typeTarget) => typeTarget.dataset.type === type,
88
- )
89
- if (target) {
90
- target.classList.remove('hidden')
44
+ invalidateTarget(target) {
45
+ const select = target.querySelector('select')
91
46
 
92
- this.validateTarget(target)
93
- }
47
+ select.setAttribute('name', '')
94
48
  }
95
49
 
96
- /**
97
- * Copy value from `valid-name` to `name`
98
- */
99
50
  validateTarget(target) {
100
- if (this.isSearchable) {
101
- const textInput = target.querySelector('input[type="text"]')
102
- const hiddenInput = target.querySelector('input[type="hidden"]')
51
+ const select = target.querySelector('select')
52
+ const validName = select.getAttribute('valid-name')
103
53
 
104
- textInput.setAttribute('name', textInput.getAttribute('valid-name'))
105
- hiddenInput.setAttribute('name', hiddenInput.getAttribute('valid-name'))
106
- } else {
107
- const select = target.querySelector('select')
108
- select.setAttribute('name', select.getAttribute('valid-name'))
109
- }
54
+ select.setAttribute('name', validName)
110
55
  }
111
56
 
112
- /**
113
- * nullify the `name` attribute
114
- */
115
- invalidateTarget(target) {
116
- if (this.isSearchable) {
117
- // Wrapping it in a try/catch to counter turbo's cache system (going back to the edit page after initial save)
118
- try {
119
- target.querySelector('input[type="text"]').setAttribute('name', '')
120
- target.querySelector('input[type="hidden"]').setAttribute('name', '')
121
- } catch {}
122
- } else if (target) {
123
- try {
124
- target.querySelector('select').setAttribute('name', '')
125
- } catch (error) {}
57
+ enableType(type) {
58
+ const target = this.typeTargets.find((typeTarget) => typeTarget.dataset.type === type)
59
+
60
+ if (target) {
61
+ target.classList.remove('hidden')
62
+ this.validateTarget(target)
126
63
  }
127
64
  }
128
65
  }
@@ -3,40 +3,17 @@ import * as Mousetrap from 'mousetrap'
3
3
  import { Controller } from 'stimulus'
4
4
  import { Turbo } from '@hotwired/turbo-rails'
5
5
  import { autocomplete } from '@algolia/autocomplete-js'
6
- import URI from 'urijs'
7
6
  import debouncePromise from '../helpers/debounce_promise'
8
7
 
9
- /**
10
- * The search controller is used in three places.
11
- * 1. Global search (on the top navbar) and can search through multiple resources.
12
- * 2. Resource search (on the Index page on top of the table panel) and will search one resource
13
- * 3. belongs_to field. This requires a bit more cleanup because the user will not navigate away from the page.
14
- * It will replace the id and label in some fields on the page and also needs a "clear" button which clears the information so the user can submit the form without a value.
15
- */
16
8
  export default class extends Controller {
17
- static targets = [
18
- 'autocomplete',
19
- 'button',
20
- 'hiddenId',
21
- 'visibleLabel',
22
- 'clearValue',
23
- 'clearButton',
24
- ];
25
-
26
- debouncedFetch = debouncePromise(fetch, this.searchDebounce);
27
-
28
- get dataset() {
29
- return this.autocompleteTarget.dataset
30
- }
9
+ static targets = ['autocomplete', 'button']
31
10
 
32
- get searchDebounce() {
33
- return window.Avo.configuration.search_debounce
34
- }
11
+ debouncedFetch = debouncePromise(fetch, this.debounceTimeout)
35
12
 
36
13
  get translationKeys() {
37
14
  let keys
38
15
  try {
39
- keys = JSON.parse(this.dataset.translationKeys)
16
+ keys = JSON.parse(this.autocompleteTarget.dataset.translationKeys)
40
17
  } catch (error) {
41
18
  keys = {}
42
19
  }
@@ -44,48 +21,20 @@ export default class extends Controller {
44
21
  return keys
45
22
  }
46
23
 
47
- get isBelongsToSearch() {
48
- return this.dataset.viaAssociation === 'belongs_to'
24
+ get debounceTimeout() {
25
+ return this.autocompleteTarget.dataset.debounceTimeout
49
26
  }
50
27
 
51
- get isGlobalSearch() {
52
- return this.dataset.searchResource === 'global'
28
+ get searchResource() {
29
+ return this.autocompleteTarget.dataset.searchResource
53
30
  }
54
31
 
55
- searchUrl(query) {
56
- const url = URI()
57
-
58
- let params = { q: query }
59
- let segments = [
60
- window.Avo.configuration.root_path,
61
- 'avo_api',
62
- this.dataset.searchResource,
63
- 'search',
64
- ]
65
-
66
- if (this.isGlobalSearch) {
67
- segments = [window.Avo.configuration.root_path, 'avo_api', 'search']
68
- }
69
-
70
- if (this.isBelongsToSearch) {
71
- // eslint-disable-next-line camelcase
72
- params = { ...params, via_association: this.dataset.viaAssociation }
73
- }
74
-
75
- return url.segment(segments).search(params).toString()
32
+ get isGlobalSearch() {
33
+ return this.searchResource === 'global'
76
34
  }
77
35
 
78
- handleOnSelect({ item }) {
79
- if (this.isBelongsToSearch) {
80
- this.hiddenIdTarget.setAttribute('value', item._id)
81
- this.buttonTarget.setAttribute('value', item._label)
82
-
83
- document.querySelector('.aa-DetachedOverlay').remove()
84
-
85
- this.clearButtonTarget.classList.remove('hidden')
86
- } else {
87
- Turbo.visit(item._url, { action: 'advance' })
88
- }
36
+ get searchUrl() {
37
+ return this.isGlobalSearch ? `${window.Avo.configuration.root_path}/avo_api/search` : `${window.Avo.configuration.root_path}/avo_api/${this.searchResource}/search`
89
38
  }
90
39
 
91
40
  addSource(resourceName, data) {
@@ -94,7 +43,9 @@ export default class extends Controller {
94
43
  return {
95
44
  sourceId: resourceName,
96
45
  getItems: () => data.results,
97
- onSelect: that.handleOnSelect.bind(that),
46
+ onSelect({ item }) {
47
+ Turbo.visit(item._url, { action: 'replace' })
48
+ },
98
49
  templates: {
99
50
  header() {
100
51
  return `${data.header.toUpperCase()} ${data.help}`
@@ -133,10 +84,7 @@ export default class extends Controller {
133
84
  })
134
85
  },
135
86
  noResults() {
136
- return that.translationKeys.no_item_found.replace(
137
- '%{item}',
138
- resourceName,
139
- )
87
+ return that.translationKeys.no_item_found.replace('%{item}', resourceName)
140
88
  },
141
89
  },
142
90
  }
@@ -146,22 +94,11 @@ export default class extends Controller {
146
94
  this.autocompleteTarget.querySelector('button').click()
147
95
  }
148
96
 
149
- clearValue() {
150
- this.clearValueTargets.map((e) => e.setAttribute('value', ''))
151
- this.clearButtonTarget.classList.add('hidden')
152
- }
153
-
154
97
  connect() {
155
98
  const that = this
156
99
 
157
100
  this.buttonTarget.onclick = () => this.showSearchPanel()
158
101
 
159
- this.clearValueTargets.forEach((target) => {
160
- if (target.getAttribute('value')) {
161
- this.clearButtonTarget.classList.remove('hidden')
162
- }
163
- })
164
-
165
102
  if (this.isGlobalSearch) {
166
103
  Mousetrap.bind(['command+k', 'ctrl+k'], () => this.showSearchPanel())
167
104
  }
@@ -175,15 +112,12 @@ export default class extends Controller {
175
112
  openOnFocus: true,
176
113
  detachedMediaQuery: '',
177
114
  getSources: ({ query }) => {
178
- const endpoint = that.searchUrl(query)
115
+ const endpoint = `${that.searchUrl}?q=${query}`
179
116
 
180
- return that
181
- .debouncedFetch(endpoint)
117
+ return that.debouncedFetch(endpoint)
182
118
  .then((response) => response.json())
183
119
  .then((data) => Object.keys(data).map((resourceName) => that.addSource(resourceName, data[resourceName])))
184
120
  },
185
121
  })
186
-
187
- this.buttonTarget.removeAttribute('disabled')
188
122
  }
189
123
  }
@@ -3,6 +3,7 @@
3
3
  data-search-target="autocomplete"
4
4
  data-search-resource="global"
5
5
  data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>", "placeholder": "<%= I18n.translate 'avo.search.placeholder' %>", "cancel_button": "<%= I18n.translate 'avo.search.cancel_button' %>"}'
6
+ data-debounce-timeout='<%= Avo.configuration.search_debounce %>'
6
7
  >
7
8
  </div>
8
9
  <div class="relative top-[-5px] inline-flex text-gray-400 text-sm leading-5 py-0.5 px-1.5 border border-gray-300 rounded-md cursor-pointer"
@@ -2,5 +2,4 @@
2
2
  window.Avo = window.Avo || { configuration: {} }
3
3
  Avo.configuration.timezone = '<%= Avo.configuration.timezone %>'
4
4
  Avo.configuration.root_path = '<%= Avo::App.root_path %>'
5
- Avo.configuration.search_debounce = '<%= Avo.configuration.search_debounce %>'
6
5
  <% end %>
@@ -3,6 +3,7 @@
3
3
  data-search-target="autocomplete"
4
4
  data-search-resource="<%= resource %>"
5
5
  data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
6
+ data-debounce-timeout='<%= Avo.configuration.search_debounce %>'
6
7
  >
7
8
  </div>
8
9
  <div class="hidden relative inline-flex text-gray-400 text-sm border border-gray-300 rounded-full cursor-pointer" data-search-target="button"></div>
data/db/factories.rb CHANGED
@@ -45,10 +45,6 @@ FactoryBot.define do
45
45
  body { Faker::Lorem.paragraphs(number: rand(4...10)).join("\n") }
46
46
  end
47
47
 
48
- factory :review do
49
- body { Faker::Lorem.paragraphs(number: rand(4...10)).join("\n") }
50
- end
51
-
52
48
  factory :person do
53
49
  name { "#{Faker::Name.first_name} #{Faker::Name.last_name}" }
54
50
  end
@@ -136,7 +136,7 @@ module Avo
136
136
  end
137
137
  end
138
138
 
139
- # Run computable callback block if present
139
+ # Run callback block if present
140
140
  if computable && block.present?
141
141
  final_value = instance_exec(@model, @resource, @view, self, &block)
142
142
  end
@@ -1,9 +1,10 @@
1
1
  module Avo
2
2
  module Fields
3
3
  class BelongsToField < BaseField
4
+ attr_reader :searchable
4
5
  attr_reader :polymorphic_as
5
6
  attr_reader :relation_method
6
- attr_reader :types # for Polymorphic associations
7
+ attr_reader :types
7
8
 
8
9
  def initialize(id, **args, &block)
9
10
  args[:placeholder] ||= I18n.t("avo.choose_an_option")
@@ -16,28 +17,10 @@ module Avo
16
17
  @relation_method = name.to_s.parameterize.underscore
17
18
  end
18
19
 
19
- def searchable
20
- @searchable && ::Avo::App.license.has_with_trial(:searchable_belongs_to)
21
- end
22
-
23
20
  def value
24
21
  super(polymorphic_as)
25
22
  end
26
23
 
27
- # The value
28
- def field_value
29
- value.send(database_value)
30
- rescue
31
- nil
32
- end
33
-
34
- # What the user sees in the text field
35
- def field_label
36
- value.send(target_resource.class.title)
37
- rescue
38
- nil
39
- end
40
-
41
24
  def options
42
25
  ::Avo::Services::AuthorizationService.apply_policy(user, target_resource.class.query_scope).all.map do |model|
43
26
  {
@@ -1,15 +1,14 @@
1
1
  module Avo
2
2
  module Fields
3
3
  class FilesField < BaseField
4
- attr_accessor :is_audio
5
4
  attr_accessor :is_image
6
5
  attr_accessor :direct_upload
7
6
 
8
7
  def initialize(id, **args, &block)
9
8
  super(id, **args, &block)
10
9
 
11
- @is_audio = args[:is_audio].present? ? args[:is_audio] : false
12
10
  @is_image = args[:is_image].present? ? args[:is_image] : @is_avatar
11
+ @is_audio = args[:is_audio].present? ? args[:is_audio] : false
13
12
  @direct_upload = args[:direct_upload].present? ? args[:direct_upload] : false
14
13
  end
15
14
 
@@ -7,8 +7,7 @@ module Avo
7
7
  :custom_tools,
8
8
  :custom_fields,
9
9
  :global_search,
10
- :enhanced_search_results,
11
- :searchable_belongs_to,
10
+ :enhanced_search_results
12
11
  ]
13
12
  end
14
13
  end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "1.20.2.pre.2"
2
+ VERSION = "1.21.0.pre.1"
3
3
  end