avo 2.3.1.pre.6 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -3
- data/app/assets/stylesheets/css/components/code.css +1 -0
- data/app/components/avo/edit/field_wrapper_component.html.erb +2 -2
- data/app/components/avo/edit/field_wrapper_component.rb +6 -1
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +3 -0
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +4 -0
- data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +26 -27
- data/app/components/avo/filters_component.html.erb +3 -3
- data/app/components/avo/filters_component.rb +2 -1
- data/app/components/avo/paginator_component.html.erb +3 -3
- data/app/components/avo/paginator_component.rb +3 -3
- data/app/components/avo/profile_item_component.html.erb +1 -1
- data/app/components/avo/profile_item_component.rb +6 -1
- data/app/components/avo/sidebar_component.html.erb +5 -2
- data/app/components/avo/sidebar_profile_component.html.erb +1 -1
- data/app/components/avo/views/resource_index_component.html.erb +3 -3
- data/app/components/avo/views/resource_index_component.rb +10 -3
- data/app/controllers/avo/associations_controller.rb +6 -1
- data/app/controllers/avo/base_controller.rb +37 -9
- data/app/controllers/avo/debug_controller.rb +3 -0
- data/app/controllers/avo/search_controller.rb +22 -1
- data/app/helpers/avo/url_helpers.rb +3 -3
- data/app/javascript/js/controllers/fields/code_field_controller.js +0 -1
- data/app/javascript/js/controllers/filter_controller.js +20 -1
- data/app/javascript/js/controllers/multiple_select_filter_controller.js +3 -1
- data/app/javascript/js/controllers/search_controller.js +9 -2
- data/app/javascript/js/controllers/select_filter_controller.js +1 -1
- data/app/views/avo/base/_boolean_filter.html.erb +23 -28
- data/app/views/avo/base/_multiple_select_filter.html.erb +7 -14
- data/app/views/avo/base/_select_filter.html.erb +6 -10
- data/app/views/avo/base/_text_filter.html.erb +7 -11
- data/app/views/avo/base/index.html.erb +1 -0
- data/app/views/avo/debug/index.html.erb +3 -3
- data/app/views/avo/debug/report.html.erb +1 -1
- data/app/views/avo/partials/_footer.html.erb +1 -1
- data/app/views/avo/partials/_navbar.html.erb +4 -1
- data/app/views/avo/partials/_sidebar_extra.html.erb +2 -0
- data/db/factories.rb +5 -3
- data/lib/avo/app.rb +37 -12
- data/lib/avo/base_resource.rb +1 -0
- data/lib/avo/configuration.rb +2 -0
- data/lib/avo/engine.rb +13 -0
- data/lib/avo/fields/belongs_to_field.rb +11 -1
- data/lib/avo/fields/code_field.rb +1 -1
- data/lib/avo/fields/has_base_field.rb +2 -0
- data/lib/avo/filters/base_filter.rb +35 -2
- data/lib/avo/filters/boolean_filter.rb +12 -0
- data/lib/avo/filters/multiple_select_filter.rb +15 -0
- data/lib/avo/filters/text_filter.rb +4 -0
- data/lib/avo/hosts/association_scope_host.rb +8 -0
- data/lib/avo/licensing/h_q.rb +52 -15
- data/lib/avo/services/authorization_service.rb +8 -10
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/eject_generator.rb +2 -3
- data/lib/generators/avo/templates/initializer/avo.tt +1 -0
- data/lib/generators/avo/templates/locales/avo.en.yml +2 -1
- data/lib/generators/avo/templates/locales/avo.nb-NO.yml +2 -1
- data/lib/generators/avo/templates/locales/avo.pt-BR.yml +2 -1
- data/lib/generators/avo/templates/locales/avo.ro.yml +2 -1
- data/lib/tasks/avo_tasks.rake +30 -0
- data/public/avo-assets/avo.css +11 -0
- data/public/avo-assets/avo.js +22 -22
- data/public/avo-assets/avo.js.map +2 -2
- metadata +5 -8
- data/app/assets/builds/avo.css +0 -8846
- data/app/assets/builds/avo.js +0 -423
- data/app/assets/builds/avo.js.map +0 -7
- data/app/mailers/avo/application_mailer.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1c24962e7af4aae8d474872c00149aac873f23dbbfad3ddf4c8de53cc4c1f75
|
4
|
+
data.tar.gz: e356866b46a51297a576a79d77cdc45f29c3482409390a95257a36797f4e759d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b946132563c3180eefa6eee7417fadb6d85658ffef574560c748ca75da3353c277b978884de218701fedf1e2db732fe129418ae26effad8e4d272223b5aeb25
|
7
|
+
data.tar.gz: 25e18c2e1de5509adfe6a051e64195d5522bfdccceecf9208458d0c0603f3452f818759056c2f24fdaedaa89788bcf00a7e17574bde09265b1c2773fece5e152
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (2.
|
4
|
+
avo (2.5.0)
|
5
5
|
active_link_to
|
6
6
|
addressable
|
7
7
|
breadcrumbs_on_rails
|
@@ -248,8 +248,6 @@ GEM
|
|
248
248
|
nokogiri (1.13.4)
|
249
249
|
mini_portile2 (~> 2.8.0)
|
250
250
|
racc (~> 1.4)
|
251
|
-
nokogiri (1.13.4-x86_64-linux)
|
252
|
-
racc (~> 1.4)
|
253
251
|
orm_adapter (0.5.0)
|
254
252
|
pagy (5.10.1)
|
255
253
|
activesupport
|
@@ -4,8 +4,8 @@
|
|
4
4
|
<% if @model.present? and @model.errors.include? @field.id %>
|
5
5
|
<div class="text-red-600 mt-2 text-sm"><%= @model.errors.full_messages_for(@field.id).to_sentence %></div>
|
6
6
|
<% end %>
|
7
|
-
<% if
|
8
|
-
<div class="text-gray-600 mt-2 text-sm"><%==
|
7
|
+
<% if help.present? %>
|
8
|
+
<div class="text-gray-600 mt-2 text-sm"><%== help %></div>
|
9
9
|
<% end %>
|
10
10
|
</div>
|
11
11
|
<% end %>
|
@@ -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: {}, label: nil, **args)
|
4
|
+
def initialize(field: nil, dash_if_blank: true, full_width: false, displayed_in_modal: false, form: nil, resource: {}, label: nil, help: nil, **args)
|
5
5
|
@field = field
|
6
6
|
@dash_if_blank = dash_if_blank
|
7
7
|
@classes = args[:class].present? ? args[:class] : ""
|
@@ -12,5 +12,10 @@ class Avo::Edit::FieldWrapperComponent < ViewComponent::Base
|
|
12
12
|
@model = resource.present? ? resource.model : nil
|
13
13
|
@full_width = full_width
|
14
14
|
@label = label
|
15
|
+
@help = help
|
16
|
+
end
|
17
|
+
|
18
|
+
def help
|
19
|
+
@help || @field.help
|
15
20
|
end
|
16
21
|
end
|
@@ -4,6 +4,9 @@
|
|
4
4
|
data-search-resource="<%= @model_key %>"
|
5
5
|
data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
|
6
6
|
data-via-association="belongs_to"
|
7
|
+
data-via-association-id="<%= @field.id %>"
|
8
|
+
data-via-reflection-id="<%= @field.model.id %>"
|
9
|
+
data-via-reflection-class="<%= @field.model.class.to_s %>"
|
7
10
|
></div>
|
8
11
|
<div class="relative w-full" autocomplete="off">
|
9
12
|
<%= @form.text_field @foreign_key,
|
@@ -34,6 +34,10 @@ class Avo::Fields::BelongsToField::AutocompleteComponent < ViewComponent::Base
|
|
34
34
|
result
|
35
35
|
end
|
36
36
|
|
37
|
+
def reflection_class
|
38
|
+
has_polymorphic_association? ? polymorphic_class : @resource.model_class._reflections[@field.id.to_s].klass
|
39
|
+
end
|
40
|
+
|
37
41
|
private
|
38
42
|
|
39
43
|
def should_prefill?
|
@@ -1,20 +1,19 @@
|
|
1
|
-
<%
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
<div class="divide-y"
|
1
|
+
<% if is_polymorphic? %>
|
2
|
+
<%
|
3
|
+
# Set the model keys so we can pass them over
|
4
|
+
model_keys = @field.types.map do |type|
|
5
|
+
resource = Avo::App.get_resource_by_model_name(type.to_s)
|
6
|
+
[type.to_s, resource.model_key]
|
7
|
+
end.to_h
|
8
|
+
%>
|
9
|
+
<div class="divide-y"
|
11
10
|
data-controller="belongs-to-field"
|
12
11
|
data-searchable="<%= @field.searchable %>"
|
13
12
|
data-association="<%= @field.id %>"
|
14
13
|
data-association-class="<%= @field&.target_resource&.model_class || nil %>"
|
15
14
|
>
|
16
|
-
|
17
|
-
|
15
|
+
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, help: @field.polymorphic_help || '' do %>
|
16
|
+
<%= @form.select @field.type_input_foreign_key, @field.types.map { |type| [type.to_s.underscore.humanize, type.to_s] },
|
18
17
|
{
|
19
18
|
value: @field.value,
|
20
19
|
include_blank: @field.placeholder,
|
@@ -26,21 +25,21 @@
|
|
26
25
|
'data-action': 'change->belongs-to-field#changeType'
|
27
26
|
}
|
28
27
|
%>
|
29
|
-
|
28
|
+
<%
|
30
29
|
# If the select field is disabled, no value will be sent. It's how HTML works.
|
31
30
|
# Thus the extra hidden field to actually send the related id to the server.
|
32
31
|
if disabled %>
|
33
|
-
|
32
|
+
<%= @form.hidden_field @field.type_input_foreign_key %>
|
33
|
+
<% end %>
|
34
34
|
<% end %>
|
35
|
-
|
36
|
-
|
37
|
-
<div class="hidden"
|
35
|
+
<% @field.types.each do |type| %>
|
36
|
+
<div class="hidden"
|
38
37
|
data-belongs-to-field-target="type"
|
39
38
|
data-type="<%= type %>"
|
40
39
|
>
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
<%= 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,
|
44
43
|
field: @field,
|
45
44
|
type: type,
|
46
45
|
model_key: model_keys[type.to_s],
|
@@ -49,8 +48,8 @@
|
|
49
48
|
disabled: disabled,
|
50
49
|
polymorphic_record: polymorphic_record
|
51
50
|
%>
|
52
|
-
|
53
|
-
|
51
|
+
<% else %>
|
52
|
+
<%= @form.select @field.id_input_foreign_key,
|
54
53
|
options_for_select(@field.values_for_type(type), @resource.present? && @resource.model.present? ? @resource.model[@field.id_input_foreign_key] : nil),
|
55
54
|
{
|
56
55
|
value: @resource.model[@field.id_input_foreign_key].to_s,
|
@@ -61,17 +60,17 @@
|
|
61
60
|
disabled: disabled
|
62
61
|
}
|
63
62
|
%>
|
64
|
-
|
63
|
+
<%
|
65
64
|
# If the select field is disabled, no value will be sent. It's how HTML works.
|
66
65
|
# Thus the extra hidden field to actually send the related id to the server.
|
67
66
|
if disabled %>
|
68
67
|
<%= @form.hidden_field @field.id_input_foreign_key %>
|
69
68
|
<% end %>
|
69
|
+
<% end %>
|
70
70
|
<% end %>
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
</div>
|
71
|
+
</div>
|
72
|
+
<% end %>
|
73
|
+
</div>
|
75
74
|
<% else %>
|
76
75
|
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
77
76
|
<% if @field.searchable %>
|
@@ -9,12 +9,12 @@
|
|
9
9
|
'data-action': 'click->toggle-panel#togglePanel',
|
10
10
|
'data-tippy': 'tooltip' do %>
|
11
11
|
<%= t 'avo.filters' %>
|
12
|
-
<% if
|
13
|
-
<span class="ml-1">(<%=
|
12
|
+
<% if @applied_filters.present? %>
|
13
|
+
<span class="ml-1">(<%= @applied_filters.count %> applied)</span>
|
14
14
|
<% end %>
|
15
15
|
<% end %>
|
16
16
|
<div
|
17
|
-
class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-20 shadow-modal rounded
|
17
|
+
class="absolute block inset-auto sm:right-0 top-full bg-white min-w-[300px] mt-2 z-20 shadow-modal rounded divide-y divide-gray-300 <%= 'hidden' unless params[:keep_filters_panel_open] %>"
|
18
18
|
data-toggle-panel-target="panel"
|
19
19
|
>
|
20
20
|
<% @filters.each do |filter| %>
|
@@ -3,9 +3,10 @@
|
|
3
3
|
class Avo::FiltersComponent < ViewComponent::Base
|
4
4
|
include Avo::ApplicationHelper
|
5
5
|
|
6
|
-
def initialize(filters: [], resource: nil)
|
6
|
+
def initialize(filters: [], resource: nil, applied_filters: [])
|
7
7
|
@filters = filters
|
8
8
|
@resource = resource
|
9
|
+
@applied_filters = applied_filters
|
9
10
|
end
|
10
11
|
|
11
12
|
def render?
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<%
|
2
2
|
per_page_options = [*Avo.configuration.per_page_steps, Avo.configuration.per_page.to_i, index_params[:per_page].to_i]
|
3
3
|
|
4
|
-
if
|
4
|
+
if parent_model.present?
|
5
5
|
per_page_options.prepend Avo.configuration.via_per_page
|
6
6
|
end
|
7
7
|
|
@@ -24,8 +24,8 @@
|
|
24
24
|
%> <%= t('avo.per_page').downcase %>
|
25
25
|
</div>
|
26
26
|
<% per_page_options.each do |option| %>
|
27
|
-
<% if
|
28
|
-
<%= link_to "Change to #{option} items per page", helpers.related_resources_path(
|
27
|
+
<% if parent_model.present? %>
|
28
|
+
<%= link_to "Change to #{option} items per page", helpers.related_resources_path(parent_model, parent_model, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
|
29
29
|
<% else %>
|
30
30
|
<%= link_to "Change to #{option} items per page", helpers.resources_path(resource: resource, per_page: option, keep_query_params: true), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
|
31
31
|
<% end %>
|
@@ -5,13 +5,13 @@ class Avo::PaginatorComponent < ViewComponent::Base
|
|
5
5
|
attr_reader :turbo_frame
|
6
6
|
attr_reader :index_params
|
7
7
|
attr_reader :resource
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :parent_model
|
9
9
|
|
10
|
-
def initialize(resource: nil,
|
10
|
+
def initialize(resource: nil, parent_model: nil, pagy: nil, turbo_frame: nil, index_params: nil)
|
11
11
|
@pagy = pagy
|
12
12
|
@turbo_frame = turbo_frame
|
13
13
|
@index_params = index_params
|
14
14
|
@resource = resource
|
15
|
-
@
|
15
|
+
@parent_model = parent_model
|
16
16
|
end
|
17
17
|
end
|
@@ -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-blue-100 block px-4 py-1 w-full py-3 text-center rounded w-full", target: target 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 %>
|
@@ -7,11 +7,16 @@ class Avo::ProfileItemComponent < ViewComponent::Base
|
|
7
7
|
attr_reader :active
|
8
8
|
attr_reader :target
|
9
9
|
|
10
|
-
def initialize(label: nil, icon: nil, path: nil, active: :inclusive, target: nil)
|
10
|
+
def initialize(label: nil, icon: nil, path: nil, active: :inclusive, target: nil, title: nil)
|
11
11
|
@label = label
|
12
12
|
@icon = icon
|
13
13
|
@path = path
|
14
14
|
@active = active
|
15
15
|
@target = target
|
16
|
+
@title = title
|
17
|
+
end
|
18
|
+
|
19
|
+
def title
|
20
|
+
@title || @label
|
16
21
|
end
|
17
22
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
-
<div
|
1
|
+
<div
|
2
|
+
class="fixed z-[60] application-sidebar hidden lg:flex h-full bg-white text-white w-64 border-r <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
|
3
|
+
data-mobile-target="sidebar"
|
4
|
+
>
|
2
5
|
<div class="flex flex-col w-full h-full">
|
3
6
|
<div class="flex justify-between">
|
4
7
|
<%= render partial: "avo/partials/logo" %>
|
@@ -10,7 +13,7 @@
|
|
10
13
|
<div class="space-y-6 mb-4">
|
11
14
|
<%= render Avo::Sidebar::LinkComponent.new label: 'Get started', path: helpers.avo.root_path, active: :exclusive if Rails.env.development? && Avo.configuration.home_path.nil? %>
|
12
15
|
|
13
|
-
<% if Avo::App.
|
16
|
+
<% if Avo::App.has_main_menu? %>
|
14
17
|
<div class="text-black">
|
15
18
|
<% Avo::App.main_menu.items.each do |item| %>
|
16
19
|
<%= render Avo::Sidebar::ItemSwitcherComponent.new item: item %>
|
@@ -25,7 +25,7 @@
|
|
25
25
|
class="hidden absolute flex flex-col inset-auto right-0 -mt-12 bg-white rounded min-w-[200px] shadow-context -translate-y-full"
|
26
26
|
data-toggle-panel-target="panel"
|
27
27
|
>
|
28
|
-
<% if Avo::App.
|
28
|
+
<% if Avo::App.has_profile_menu? %>
|
29
29
|
<div class="text-black space-y-4">
|
30
30
|
<% Avo::App.profile_menu.items.each do |item| %>
|
31
31
|
<% if item.is_a? Avo::Menu::Link %>
|
@@ -30,7 +30,7 @@
|
|
30
30
|
<% end %>
|
31
31
|
<% if @filters.present? || available_view_types.count > 1 %>
|
32
32
|
<div class="justify-self-end flex justify-start xs:justify-end items-center px-4 space-x-3">
|
33
|
-
<%= render Avo::FiltersComponent.new filters: @filters, resource: @resource %>
|
33
|
+
<%= render Avo::FiltersComponent.new filters: @filters, resource: @resource, applied_filters: @applied_filters %>
|
34
34
|
<%= render partial: 'avo/partials/view_toggle_button', locals: { available_view_types: available_view_types, view_type: view_type, turbo_frame: @turbo_frame } if available_view_types.count > 1 %>
|
35
35
|
</div>
|
36
36
|
<% end %>
|
@@ -51,7 +51,7 @@
|
|
51
51
|
<% if view_type.to_sym == :table %>
|
52
52
|
<% if @models.present? %>
|
53
53
|
<div class="mt-8">
|
54
|
-
<%= render Avo::PaginatorComponent.new pagy: @pagy, turbo_frame: @turbo_frame || 'none', index_params: @index_params, resource: @resource,
|
54
|
+
<%= render Avo::PaginatorComponent.new pagy: @pagy, turbo_frame: @turbo_frame || 'none', index_params: @index_params, resource: @resource, parent_model: @parent_model %>
|
55
55
|
</div>
|
56
56
|
<% end %>
|
57
57
|
<% end %>
|
@@ -59,7 +59,7 @@
|
|
59
59
|
<% if view_type.to_sym == :grid %>
|
60
60
|
<%= render Avo::Index::ResourceGridComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model) %>
|
61
61
|
<div class="mt-14">
|
62
|
-
<%= render Avo::PaginatorComponent.new pagy: @pagy, turbo_frame: @turbo_frame || 'none', index_params: @index_params, resource: @resource,
|
62
|
+
<%= render Avo::PaginatorComponent.new pagy: @pagy, turbo_frame: @turbo_frame || 'none', index_params: @index_params, resource: @resource, parent_model: @parent_model %>
|
63
63
|
</div>
|
64
64
|
<% end %>
|
65
65
|
<% end %>
|
@@ -14,7 +14,8 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
|
|
14
14
|
actions: [],
|
15
15
|
reflection: nil,
|
16
16
|
turbo_frame: "",
|
17
|
-
parent_model: nil
|
17
|
+
parent_model: nil,
|
18
|
+
applied_filters: []
|
18
19
|
)
|
19
20
|
@resource = resource
|
20
21
|
@resources = resources
|
@@ -26,6 +27,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
|
|
26
27
|
@reflection = reflection
|
27
28
|
@turbo_frame = turbo_frame
|
28
29
|
@parent_model = parent_model
|
30
|
+
@applied_filters = applied_filters
|
29
31
|
end
|
30
32
|
|
31
33
|
def title
|
@@ -90,7 +92,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
|
|
90
92
|
if @reflection.present?
|
91
93
|
args = {
|
92
94
|
via_relation_class: reflection_model_class,
|
93
|
-
via_resource_id: @parent_model.id
|
95
|
+
via_resource_id: @parent_model.id
|
94
96
|
}
|
95
97
|
|
96
98
|
if @reflection.is_a? ActiveRecord::Reflection::ThroughReflection
|
@@ -126,7 +128,12 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
|
|
126
128
|
end
|
127
129
|
|
128
130
|
def description
|
129
|
-
|
131
|
+
# If this is a has many association, the user can pass a description to be shown just for this association.
|
132
|
+
if @reflection.present?
|
133
|
+
return field.description if field.present? && field.description
|
134
|
+
|
135
|
+
return
|
136
|
+
end
|
130
137
|
|
131
138
|
@resource.resource_description
|
132
139
|
end
|
@@ -21,7 +21,7 @@ module Avo
|
|
21
21
|
@association_field = @parent_resource.get_field params[:related_name]
|
22
22
|
|
23
23
|
if @association_field.present? && @association_field.scope.present?
|
24
|
-
@query =
|
24
|
+
@query = Avo::Hosts::AssociationScopeHost.new(block: @association_field.scope, query: @query, parent: @parent_model).handle
|
25
25
|
end
|
26
26
|
|
27
27
|
super
|
@@ -45,6 +45,11 @@ module Avo
|
|
45
45
|
if @field.present? && !@field.searchable
|
46
46
|
query = @authorization.apply_policy @attachment_class
|
47
47
|
|
48
|
+
# Add the association scope to the query scope
|
49
|
+
if @field.scope.present?
|
50
|
+
query = Avo::Hosts::AssociationScopeHost.new(block: @field.scope, query: query, parent: @model).handle
|
51
|
+
end
|
52
|
+
|
48
53
|
@options = query.all.map do |model|
|
49
54
|
[model.send(@attachment_resource.class.title), model.id]
|
50
55
|
end
|
@@ -5,6 +5,7 @@ module Avo
|
|
5
5
|
before_action :set_resource_name
|
6
6
|
before_action :set_resource
|
7
7
|
before_action :hydrate_resource
|
8
|
+
before_action :set_applied_filters, only: :index
|
8
9
|
before_action :set_model, only: [:show, :edit, :destroy, :update, :order]
|
9
10
|
before_action :set_model_to_fill
|
10
11
|
before_action :set_edit_title_and_breadcrumbs, only: [:edit, :update]
|
@@ -55,8 +56,8 @@ module Avo
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
58
|
-
# Apply filters
|
59
|
-
|
59
|
+
# Apply filters to the current query
|
60
|
+
filters_to_be_applied.each do |filter_class, filter_value|
|
60
61
|
@query = filter_class.safe_constantize.new.apply_query request, @query, filter_value
|
61
62
|
end
|
62
63
|
|
@@ -308,28 +309,55 @@ module Avo
|
|
308
309
|
.select { |action| action.visible_in_view }
|
309
310
|
end
|
310
311
|
|
311
|
-
def
|
312
|
-
|
313
|
-
|
314
|
-
|
312
|
+
def set_applied_filters
|
313
|
+
@applied_filters = Avo::Filters::BaseFilter.decode_filters(params[Avo::Filters::BaseFilter::PARAM_KEY])
|
314
|
+
|
315
|
+
# Some filters react to others and will have to be merged into this
|
316
|
+
@applied_filters = @applied_filters.merge reactive_filters
|
317
|
+
rescue
|
318
|
+
@applied_filters = {}
|
319
|
+
end
|
320
|
+
|
321
|
+
def reactive_filters
|
322
|
+
filter_reactions = {}
|
323
|
+
|
324
|
+
# Go through all filters
|
325
|
+
@resource.get_filters
|
326
|
+
.select do |filter_class|
|
327
|
+
filter_class.instance_methods(false).include? :react
|
328
|
+
end
|
329
|
+
.each do |filter_class|
|
330
|
+
# Run the react method if it's present
|
331
|
+
reaction = filter_class.new.react
|
332
|
+
|
333
|
+
next if reaction.nil?
|
334
|
+
|
335
|
+
filter_reactions[filter_class.to_s] = filter_class.new.react
|
336
|
+
end
|
337
|
+
|
338
|
+
filter_reactions
|
339
|
+
end
|
315
340
|
|
341
|
+
# Get the default state of the filters and override with the user applied filters
|
342
|
+
def filters_to_be_applied
|
316
343
|
filter_defaults = {}
|
317
344
|
|
318
345
|
@resource.get_filters.each do |filter_class|
|
319
346
|
filter = filter_class.new
|
320
347
|
|
321
|
-
|
348
|
+
unless filter.default.nil?
|
322
349
|
filter_defaults[filter_class.to_s] = filter.default
|
323
350
|
end
|
324
351
|
end
|
325
352
|
|
326
|
-
filter_defaults
|
353
|
+
filter_defaults.merge(@applied_filters)
|
327
354
|
end
|
328
355
|
|
356
|
+
# Caching these so we know when the filters have changed so we reset the pagination
|
329
357
|
def cache_applied_filters
|
330
358
|
::Avo::App.cache_store.delete applied_filters_cache_key if params[:filters].nil?
|
331
359
|
|
332
|
-
::Avo::App.cache_store.write(applied_filters_cache_key, params[:filters], expires_in:
|
360
|
+
::Avo::App.cache_store.write(applied_filters_cache_key, params[:filters], expires_in: 1.day)
|
333
361
|
end
|
334
362
|
|
335
363
|
def reset_pagination_if_filters_changed
|
@@ -44,7 +44,23 @@ module Avo
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def search_resource(resource)
|
47
|
-
|
47
|
+
query = resource.search_query.call(params: params).limit(8)
|
48
|
+
|
49
|
+
# Figure oute if this is a belongs_to search
|
50
|
+
if params[:via_reflection_class].present? && params[:via_reflection_id].present?
|
51
|
+
# Fetch the field
|
52
|
+
field = belongs_to_field
|
53
|
+
|
54
|
+
if field.scope.present?
|
55
|
+
# Fetch the parent
|
56
|
+
parent = params[:via_reflection_class].safe_constantize.find params[:via_reflection_id]
|
57
|
+
|
58
|
+
# Add to the query
|
59
|
+
query = Avo::Hosts::AssociationScopeHost.new(block: belongs_to_field.scope, query: query, parent: parent).handle
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
results = apply_search_metadata(query, resource)
|
48
64
|
|
49
65
|
result_object = {
|
50
66
|
header: resource.name.pluralize,
|
@@ -75,5 +91,10 @@ module Avo
|
|
75
91
|
result
|
76
92
|
end
|
77
93
|
end
|
94
|
+
|
95
|
+
def belongs_to_field
|
96
|
+
fields = ::Avo::App.get_resource_by_model_name(params[:via_reflection_class]).get_field_definitions
|
97
|
+
fields.find { |f| f.id.to_s == params[:via_association_id] }
|
98
|
+
end
|
78
99
|
end
|
79
100
|
end
|
@@ -59,11 +59,11 @@ module Avo
|
|
59
59
|
|
60
60
|
def related_resources_path(
|
61
61
|
parent_model,
|
62
|
-
|
62
|
+
record,
|
63
63
|
keep_query_params: false,
|
64
64
|
**args
|
65
65
|
)
|
66
|
-
return if
|
66
|
+
return if record.nil?
|
67
67
|
|
68
68
|
existing_params = {}
|
69
69
|
|
@@ -75,7 +75,7 @@ module Avo
|
|
75
75
|
rescue
|
76
76
|
end
|
77
77
|
|
78
|
-
avo.resources_associations_index_path(
|
78
|
+
avo.resources_associations_index_path(parent_model.model_name.route_key, record.id, **existing_params, **args)
|
79
79
|
end
|
80
80
|
|
81
81
|
def order_up_resource_path(model:, resource:, **args)
|
@@ -4,6 +4,10 @@ import URI from 'urijs'
|
|
4
4
|
export default class extends Controller {
|
5
5
|
static targets = ['urlRedirect']
|
6
6
|
|
7
|
+
static values = {
|
8
|
+
keepFiltersPanelOpen: Boolean,
|
9
|
+
}
|
10
|
+
|
7
11
|
// eslint-disable-next-line class-methods-use-this
|
8
12
|
uriParams() {
|
9
13
|
return URI(window.location.toString()).query(true)
|
@@ -38,18 +42,22 @@ export default class extends Controller {
|
|
38
42
|
const value = this.getFilterValue()
|
39
43
|
const filterClass = this.getFilterClass()
|
40
44
|
|
45
|
+
// Get the `filters` param for all params
|
41
46
|
let filters = this.uriParams()[this.uriParam('filters')]
|
42
47
|
|
48
|
+
// Decode the filters
|
43
49
|
if (filters) {
|
44
50
|
filters = JSON.parse(this.b64DecodeUnicode(filters))
|
45
51
|
} else {
|
46
52
|
filters = {}
|
47
53
|
}
|
48
54
|
|
55
|
+
// Get the values for this particular filter
|
49
56
|
filters[filterClass] = value
|
50
57
|
|
51
58
|
const filtered = Object.keys(filters)
|
52
|
-
|
59
|
+
// Filter out the filters without a value
|
60
|
+
.filter((key) => filters[key] !== null)
|
53
61
|
.reduce((obj, key) => {
|
54
62
|
obj[key] = filters[key]
|
55
63
|
|
@@ -58,16 +66,27 @@ export default class extends Controller {
|
|
58
66
|
|
59
67
|
let encodedFilters
|
60
68
|
|
69
|
+
// Encode the filters and their values
|
61
70
|
if (filtered && Object.keys(filtered).length > 0) {
|
62
71
|
encodedFilters = this.b64EncodeUnicode(JSON.stringify(filtered))
|
63
72
|
}
|
64
73
|
|
74
|
+
this.navigateToURLWithFilters(encodedFilters)
|
75
|
+
}
|
76
|
+
|
77
|
+
navigateToURLWithFilters(encodedFilters) {
|
78
|
+
// Create a new URI with them
|
65
79
|
const url = new URI(this.urlRedirectTarget.href)
|
66
80
|
|
67
81
|
const query = {
|
68
82
|
...url.query(true),
|
69
83
|
}
|
70
84
|
|
85
|
+
if (this.keepFiltersPanelOpenValue) {
|
86
|
+
// eslint-disable-next-line camelcase
|
87
|
+
query.keep_filters_panel_open = this.keepFiltersPanelOpenValue
|
88
|
+
}
|
89
|
+
|
71
90
|
if (encodedFilters) {
|
72
91
|
query.filters = encodedFilters
|
73
92
|
} else {
|
@@ -4,7 +4,9 @@ export default class extends BaseFilterController {
|
|
4
4
|
static targets = ['selector']
|
5
5
|
|
6
6
|
getFilterValue() {
|
7
|
-
|
7
|
+
const filterValue = Array.from(this.selectorTarget.selectedOptions).map(({ value }) => value)
|
8
|
+
|
9
|
+
return filterValue.length === 0 ? null : filterValue
|
8
10
|
}
|
9
11
|
|
10
12
|
getFilterClass() {
|