avo 1.6.3.pre.2 → 1.7.2
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/app/components/avo/edit/field_wrapper_component.html.erb +1 -1
- data/app/components/avo/edit/field_wrapper_component.rb +6 -1
- data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +65 -15
- data/app/components/avo/fields/belongs_to_field/edit_component.rb +1 -1
- data/app/components/avo/fields/common/badge_viewer_component.html.erb +1 -1
- data/app/controllers/avo/base_controller.rb +5 -0
- data/app/packs/js/controllers/fields/belongs_to_field_controller.js +65 -0
- data/lib/avo/base_resource.rb +23 -37
- data/lib/avo/fields/base_field.rb +1 -1
- data/lib/avo/fields/belongs_to_field.rb +48 -6
- data/lib/avo/fields/boolean_group_field.rb +1 -1
- data/lib/avo/fields/date_time_field.rb +1 -1
- data/lib/avo/fields/files_field.rb +1 -1
- data/lib/avo/fields/has_one_field.rb +1 -1
- data/lib/avo/fields/key_value_field.rb +1 -1
- data/lib/avo/fields/select_field.rb +17 -4
- data/lib/avo/fields_collector.rb +0 -2
- data/lib/avo/version.rb +1 -1
- data/public/avo-packs/css/application-797341b7.css.map +1 -1
- data/public/avo-packs/css/application-797341b7.css.map.br +0 -0
- data/public/avo-packs/css/application-797341b7.css.map.gz +0 -0
- data/public/avo-packs/js/application-2a4345ecd9464d1e8850.js +26 -0
- data/public/avo-packs/js/{application-b444cbf11135b4b23654.js.LICENSE.txt → application-2a4345ecd9464d1e8850.js.LICENSE.txt} +0 -0
- data/public/avo-packs/js/application-2a4345ecd9464d1e8850.js.br +0 -0
- data/public/avo-packs/js/application-2a4345ecd9464d1e8850.js.gz +0 -0
- data/public/avo-packs/js/application-2a4345ecd9464d1e8850.js.map +1 -0
- data/public/avo-packs/js/application-2a4345ecd9464d1e8850.js.map.br +0 -0
- data/public/avo-packs/js/application-2a4345ecd9464d1e8850.js.map.gz +0 -0
- data/public/avo-packs/manifest.json +8 -8
- metadata +12 -11
- data/public/avo-packs/js/application-b444cbf11135b4b23654.js +0 -26
- data/public/avo-packs/js/application-b444cbf11135b4b23654.js.br +0 -0
- data/public/avo-packs/js/application-b444cbf11135b4b23654.js.gz +0 -0
- data/public/avo-packs/js/application-b444cbf11135b4b23654.js.map +0 -1
- data/public/avo-packs/js/application-b444cbf11135b4b23654.js.map.br +0 -0
- data/public/avo-packs/js/application-b444cbf11135b4b23654.js.map.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76e6f84b0b7d8c9144994b898621dd99359875e7a32e26865c86034431031992
|
4
|
+
data.tar.gz: 8df9ac786016d2ba0ef2ca2071a4d9e3445a6d3e0db3d55fa4ac12aa49d1fe79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ef42c3022b80c94f2fa5543321a737d7c89fe4a57ee81493fdd3c9cd068184c8107873985e9df3a0d8d33fef2ccf664f7bd70094349925a6b04f94375cb0e92
|
7
|
+
data.tar.gz: 68fa168d75070ff5dd08f6b5643ecf9efa73a003b5fb3968bb8b784594553cf76043a8598397cb6a392fcae958865f52f530126df3825df3f132656dbfe3b770
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (1.
|
4
|
+
avo (1.7.2)
|
5
5
|
active_link_to
|
6
6
|
addressable
|
7
7
|
breadcrumbs_on_rails
|
@@ -333,7 +333,7 @@ GEM
|
|
333
333
|
thread_safe (~> 0.1)
|
334
334
|
unicode-display_width (2.0.0)
|
335
335
|
unicode_utils (1.4.0)
|
336
|
-
view_component (2.
|
336
|
+
view_component (2.32.0)
|
337
337
|
activesupport (>= 5.0.0, < 7.0)
|
338
338
|
warden (1.2.9)
|
339
339
|
rack (>= 2.0.9)
|
data/README.md
CHANGED
@@ -56,6 +56,6 @@ $ bundle install
|
|
56
56
|
|
57
57
|
Please read [CONTRIBUTING.MD](./CONTRIBUTING.MD)
|
58
58
|
|
59
|
-
# Upgrade
|
59
|
+
# Upgrade Guide
|
60
60
|
|
61
|
-
Please read the [UPGRADE_GUIDE.MD](https://docs.avohq.io/0
|
61
|
+
Please read the [UPGRADE_GUIDE.MD](https://docs.avohq.io/1.0/upgrade.html)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<div class="flex items-center py-0 leading-tight <%= @classes %> min-h-16" data-field-id="<%= @field.id %>" data-field-type="<%= @field.type %>">
|
2
2
|
<div class="h-16 flex self-start items-center text-blue-gray-800">
|
3
3
|
<div class="<% if @displayed_in_modal %> md:w-48 <% else %> md:w-64 <% end %> w-48 px-8 flex" data-slot="label">
|
4
|
-
<%= @form.label @field.id,
|
4
|
+
<%= @form.label @field.id, label %> <% if @field.required %> <span class="text-red-600">*</span> <% end %>
|
5
5
|
</div>
|
6
6
|
</div>
|
7
7
|
<div class="flex-1 flex flex-row min-h-inherit">
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Avo::Edit::FieldWrapperComponent < ViewComponent::Base
|
4
|
-
def initialize(field: nil, dash_if_blank: true, full_width: false, displayed_in_modal: false, form: nil, resource: {}, **args)
|
4
|
+
def initialize(field: nil, dash_if_blank: true, full_width: false, displayed_in_modal: false, form: nil, resource: {}, label: nil, **args)
|
5
5
|
@field = field
|
6
6
|
@dash_if_blank = dash_if_blank
|
7
7
|
@classes = args[:class].present? ? args[:class] : ""
|
@@ -11,9 +11,14 @@ class Avo::Edit::FieldWrapperComponent < ViewComponent::Base
|
|
11
11
|
@resource = resource
|
12
12
|
@model = resource.present? ? resource.model : nil
|
13
13
|
@full_width = full_width
|
14
|
+
@label = label
|
14
15
|
|
15
16
|
if (@index != 0) || @displayed_in_modal
|
16
17
|
@classes += " border-t"
|
17
18
|
end
|
18
19
|
end
|
20
|
+
|
21
|
+
def label
|
22
|
+
@label || @field.name
|
23
|
+
end
|
19
24
|
end
|
@@ -1,18 +1,68 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
<% if @field.types.present? %>
|
2
|
+
<div data-controller="belongs-to-field">
|
3
|
+
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
4
|
+
<%= @form.select "#{@field.foreign_key}_type", @field.types.map { |type| [type.to_s.underscore.humanize, type.to_s] },
|
5
|
+
{
|
6
|
+
include_blank: @field.placeholder,
|
7
|
+
},
|
8
|
+
{
|
9
|
+
class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
|
10
|
+
disabled: disabled,
|
11
|
+
'data-belongs-to-field-target': "select",
|
12
|
+
'data-action': 'change->belongs-to-field#changedType'
|
13
|
+
}
|
14
|
+
%>
|
15
|
+
<%
|
16
|
+
# If the select field is disabled, no value will be sent. It's how HTML works.
|
17
|
+
# Thus the extra hidden field to actually send the related id to the server.
|
18
|
+
if disabled
|
19
|
+
%>
|
20
|
+
<%= @form.hidden_field "#{@field.foreign_key}_type" %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
<% @field.types.each do |type| %>
|
24
|
+
<div class="hidden"
|
25
|
+
data-belongs-to-field-target="type"
|
26
|
+
data-type="<%= type %>"
|
27
|
+
>
|
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 %>
|
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
|
+
}
|
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" %>
|
44
|
+
<% end %>
|
45
|
+
<% end %>
|
46
|
+
</div>
|
47
|
+
<% end %>
|
48
|
+
</div>
|
49
|
+
<% else %>
|
50
|
+
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
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
|
+
}
|
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
|
10
64
|
%>
|
11
|
-
|
12
|
-
|
13
|
-
# Thus the extra hidden field to actually send the related id to the server.
|
14
|
-
if disabled
|
15
|
-
%>
|
16
|
-
<%= @form.hidden_field @field.foreign_key %>
|
65
|
+
<%= @form.hidden_field @field.foreign_key %>
|
66
|
+
<% end %>
|
17
67
|
<% end %>
|
18
68
|
<% end %>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
|
4
4
|
def disabled
|
5
5
|
return true if @field.readonly
|
6
|
-
return true if @field.target_resource.model_class.name == params[:via_resource_class]
|
6
|
+
return true if @field.target_resource.present? && @field.target_resource.model_class.name == params[:via_resource_class]
|
7
7
|
return true if @field.id.to_s == params[:via_relation].to_s
|
8
8
|
|
9
9
|
false
|
@@ -21,6 +21,11 @@ module Avo
|
|
21
21
|
@query = @authorization.apply_policy @resource.model_class
|
22
22
|
end
|
23
23
|
|
24
|
+
# Remove default_scope for index view
|
25
|
+
if @resource.unscoped_queries_on_index
|
26
|
+
@query = @query.unscoped
|
27
|
+
end
|
28
|
+
|
24
29
|
# Eager load the relations
|
25
30
|
if @resource.includes.present?
|
26
31
|
@query = @query.includes(*@resource.includes)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import { Controller } from 'stimulus'
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ['select', 'type']
|
5
|
+
|
6
|
+
get selectedType() {
|
7
|
+
return this.selectTarget.value
|
8
|
+
}
|
9
|
+
|
10
|
+
connect() {
|
11
|
+
this.setValidNames()
|
12
|
+
this.changedType()
|
13
|
+
}
|
14
|
+
|
15
|
+
setValidNames() {
|
16
|
+
this.typeTargets.forEach((target) => {
|
17
|
+
const { type } = target.dataset
|
18
|
+
const select = target.querySelector('select')
|
19
|
+
const name = select.getAttribute('name')
|
20
|
+
|
21
|
+
select.setAttribute('valid-name', name)
|
22
|
+
if (this.selectedType !== type) {
|
23
|
+
select.selectedIndex = 0
|
24
|
+
}
|
25
|
+
})
|
26
|
+
}
|
27
|
+
|
28
|
+
changedType() {
|
29
|
+
this.hideAllTypeTargets()
|
30
|
+
this.enableType(this.selectTarget.value)
|
31
|
+
}
|
32
|
+
|
33
|
+
hideAllTypeTargets() {
|
34
|
+
this.typeTargets.forEach((target) => {
|
35
|
+
this.hideTarget(target)
|
36
|
+
this.invalidateTarget(target)
|
37
|
+
})
|
38
|
+
}
|
39
|
+
|
40
|
+
hideTarget(target) {
|
41
|
+
target.classList.add('hidden')
|
42
|
+
}
|
43
|
+
|
44
|
+
invalidateTarget(target) {
|
45
|
+
const select = target.querySelector('select')
|
46
|
+
|
47
|
+
select.setAttribute('name', '')
|
48
|
+
}
|
49
|
+
|
50
|
+
validateTarget(target) {
|
51
|
+
const select = target.querySelector('select')
|
52
|
+
const validName = select.getAttribute('valid-name')
|
53
|
+
|
54
|
+
select.setAttribute('name', validName)
|
55
|
+
}
|
56
|
+
|
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)
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
data/lib/avo/base_resource.rb
CHANGED
@@ -22,6 +22,7 @@ module Avo
|
|
22
22
|
class_attribute :fields
|
23
23
|
class_attribute :grid_loader
|
24
24
|
class_attribute :visible_on_sidebar, default: true
|
25
|
+
class_attribute :unscoped_queries_on_index, default: false
|
25
26
|
|
26
27
|
class << self
|
27
28
|
def grid(&block)
|
@@ -77,7 +78,7 @@ module Avo
|
|
77
78
|
field.hydrate(resource: self, panel_name: default_panel_name, user: user)
|
78
79
|
end
|
79
80
|
|
80
|
-
if Avo::App.license.
|
81
|
+
if Avo::App.license.lacks_with_trial(:custom_fields)
|
81
82
|
fields = fields.reject do |field|
|
82
83
|
field.custom?
|
83
84
|
end
|
@@ -95,13 +96,14 @@ module Avo
|
|
95
96
|
field.visible?
|
96
97
|
end
|
97
98
|
.select do |field|
|
98
|
-
|
99
|
-
|
100
|
-
|
99
|
+
# Strip out the reflection field in index queries with a parent association.
|
100
|
+
if reflection.present? &&
|
101
|
+
reflection.options.present? &&
|
102
|
+
field.respond_to?(:polymorphic_as) &&
|
103
|
+
field.polymorphic_as.to_s == reflection.options[:as].to_s
|
101
104
|
next
|
102
105
|
end
|
103
|
-
if
|
104
|
-
field.respond_to?(:foreign_key) &&
|
106
|
+
if field.respond_to?(:foreign_key) &&
|
105
107
|
reflection.present? &&
|
106
108
|
reflection.respond_to?(:foreign_key) &&
|
107
109
|
reflection.foreign_key != field.foreign_key
|
@@ -225,28 +227,6 @@ module Avo
|
|
225
227
|
self.class.context
|
226
228
|
end
|
227
229
|
|
228
|
-
def query_search(via_resource_name:, via_resource_id:, user:, query: "")
|
229
|
-
# model_class = self.model
|
230
|
-
|
231
|
-
db_query = AuthorizationService.apply_policy(user, model_class)
|
232
|
-
|
233
|
-
if via_resource_name.present?
|
234
|
-
related_model = App.get_resource_by_name(via_resource_name).model
|
235
|
-
|
236
|
-
db_query = related_model.find(via_resource_id).public_send(plural_name.downcase)
|
237
|
-
end
|
238
|
-
|
239
|
-
new_query = []
|
240
|
-
|
241
|
-
[search].flatten.each_with_index do |search_by, index|
|
242
|
-
new_query.push "or" if index != 0
|
243
|
-
|
244
|
-
new_query.push "text(#{search_by}) ILIKE '%#{query}%'"
|
245
|
-
end
|
246
|
-
|
247
|
-
db_query.where(new_query.join(" "))
|
248
|
-
end
|
249
|
-
|
250
230
|
def attached_file_fields
|
251
231
|
get_field_definitions.select do |field|
|
252
232
|
[Avo::Fields::FileField, Avo::Fields::FilesField].include? field.class
|
@@ -259,14 +239,17 @@ module Avo
|
|
259
239
|
.reject do |field|
|
260
240
|
field.computed
|
261
241
|
end
|
262
|
-
.map
|
242
|
+
.map do |field|
|
243
|
+
[field.database_id(model).to_s, field]
|
244
|
+
end
|
245
|
+
.to_h
|
263
246
|
|
264
247
|
params.each do |key, value|
|
265
248
|
field = fields_by_database_id[key]
|
266
249
|
|
267
250
|
next unless field.present?
|
268
251
|
|
269
|
-
model = field.fill_field model, key, value
|
252
|
+
model = field.fill_field model, key, value, params
|
270
253
|
end
|
271
254
|
|
272
255
|
model
|
@@ -304,19 +287,22 @@ module Avo
|
|
304
287
|
|
305
288
|
# We will not overwrite any attributes that come pre-filled in the model.
|
306
289
|
def hydrate_model_with_default_values
|
307
|
-
default_values = get_fields
|
308
|
-
|
309
|
-
|
290
|
+
default_values = get_fields
|
291
|
+
.select do |field|
|
292
|
+
!field.computed
|
293
|
+
end
|
310
294
|
.map do |field|
|
311
295
|
id = field.id
|
312
296
|
value = field.value
|
313
297
|
|
314
|
-
if field.
|
298
|
+
if field.type == "belongs_to"
|
315
299
|
id = field.foreign_key.to_sym
|
316
300
|
|
317
301
|
reflection = @model._reflections[@params[:via_relation]]
|
318
302
|
|
319
|
-
if
|
303
|
+
if field.polymorphic_as.present? && field.types.map(&:to_s).include?(@params["via_relation_class"])
|
304
|
+
value = @params["via_relation_class"].safe_constantize.find(@params[:via_resource_id])
|
305
|
+
elsif reflection.present? && reflection.foreign_key.present? && field.id.to_s == @params[:via_relation].to_s
|
320
306
|
value = @params[:via_resource_id]
|
321
307
|
end
|
322
308
|
end
|
@@ -325,8 +311,8 @@ module Avo
|
|
325
311
|
end
|
326
312
|
.to_h
|
327
313
|
.select do |id, value|
|
328
|
-
|
329
|
-
|
314
|
+
value.present?
|
315
|
+
end
|
330
316
|
|
331
317
|
default_values.each do |id, value|
|
332
318
|
if @model.send(id).nil?
|
@@ -3,8 +3,8 @@ module Avo
|
|
3
3
|
class BelongsToField < BaseField
|
4
4
|
attr_reader :searchable
|
5
5
|
attr_reader :polymorphic_as
|
6
|
-
attr_reader :polymorphic_for
|
7
6
|
attr_reader :relation_method
|
7
|
+
attr_reader :types
|
8
8
|
|
9
9
|
def initialize(id, **args, &block)
|
10
10
|
args[:placeholder] ||= I18n.t("avo.choose_an_option")
|
@@ -13,10 +13,8 @@ module Avo
|
|
13
13
|
|
14
14
|
@searchable = args[:searchable] == true
|
15
15
|
@polymorphic_as = args[:polymorphic_as]
|
16
|
-
@
|
16
|
+
@types = args[:types]
|
17
17
|
@relation_method = name.to_s.parameterize.underscore
|
18
|
-
|
19
|
-
hide_on(:edit, :new) if polymorphic_as.present?
|
20
18
|
end
|
21
19
|
|
22
20
|
def value
|
@@ -24,7 +22,7 @@ module Avo
|
|
24
22
|
end
|
25
23
|
|
26
24
|
def options
|
27
|
-
target_resource.model_class.all.map do |model|
|
25
|
+
::Avo::Services::AuthorizationService.apply_policy(user, target_resource.model_class).all.map do |model|
|
28
26
|
{
|
29
27
|
value: model.id,
|
30
28
|
label: model.send(target_resource.class.title)
|
@@ -32,6 +30,12 @@ module Avo
|
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
33
|
+
def values_for_type(type)
|
34
|
+
::Avo::Services::AuthorizationService.apply_policy(user, type).all.map do |model|
|
35
|
+
[model.send(App.get_resource_by_model_name(type).class.title), model.id]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
35
39
|
def database_value
|
36
40
|
target_resource.id
|
37
41
|
end
|
@@ -61,11 +65,49 @@ module Avo
|
|
61
65
|
end
|
62
66
|
|
63
67
|
def to_permitted_param
|
68
|
+
if polymorphic_as.present?
|
69
|
+
return ["#{polymorphic_as}_type".to_sym, "#{polymorphic_as}_id".to_sym]
|
70
|
+
end
|
71
|
+
|
64
72
|
foreign_key.to_sym
|
65
73
|
end
|
66
74
|
|
75
|
+
def fill_field(model, key, value, params)
|
76
|
+
return model unless model.methods.include? key.to_sym
|
77
|
+
|
78
|
+
if polymorphic_as.present?
|
79
|
+
model.send("#{polymorphic_as}_type=", params["#{polymorphic_as}_type"])
|
80
|
+
|
81
|
+
# If the type is blank, reset the id too.
|
82
|
+
if params["#{polymorphic_as}_type"].blank?
|
83
|
+
model.send("#{polymorphic_as}_id=", nil)
|
84
|
+
else
|
85
|
+
model.send("#{polymorphic_as}_id=", params["#{polymorphic_as}_id"])
|
86
|
+
end
|
87
|
+
else
|
88
|
+
model.send("#{key}=", value)
|
89
|
+
end
|
90
|
+
|
91
|
+
model
|
92
|
+
end
|
93
|
+
|
94
|
+
def database_id(model)
|
95
|
+
# If the field is a polymorphic value, return the polymorphic_type as key and pre-fill the _id in fill_field.
|
96
|
+
return "#{polymorphic_as}_type" if polymorphic_as.present?
|
97
|
+
|
98
|
+
foreign_key
|
99
|
+
rescue
|
100
|
+
id
|
101
|
+
end
|
102
|
+
|
67
103
|
def target_resource
|
68
|
-
|
104
|
+
if polymorphic_as.present?
|
105
|
+
if value.present?
|
106
|
+
return App.get_resource_by_model_name(value.class)
|
107
|
+
else
|
108
|
+
return nil
|
109
|
+
end
|
110
|
+
end
|
69
111
|
|
70
112
|
reflection_key = polymorphic_as || id
|
71
113
|
|