avo 2.19.0 → 2.20.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 +1 -2
- data/Gemfile.lock +6 -6
- data/app/components/avo/card_component.html.erb +15 -15
- data/app/components/avo/card_component.rb +7 -5
- data/app/components/avo/fields/select_field/edit_component.html.erb +1 -1
- data/app/components/avo/panel_component.html.erb +1 -1
- data/app/components/avo/sidebar/group_component.html.erb +18 -12
- data/app/components/avo/sidebar/heading_component.html.erb +17 -10
- data/app/controllers/avo/actions_controller.rb +27 -5
- data/app/controllers/avo/application_controller.rb +1 -0
- data/app/controllers/avo/base_controller.rb +18 -13
- data/app/controllers/avo/dashboards_controller.rb +1 -0
- data/app/views/avo/actions/keep_modal_open.turbo_stream.erb +5 -0
- data/app/views/avo/actions/show.html.erb +2 -1
- data/app/views/layouts/avo/application.html.erb +1 -1
- data/avo.gemspec +0 -1
- data/lib/avo/app.rb +4 -1
- data/lib/avo/base_action.rb +16 -1
- data/lib/avo/base_card.rb +3 -1
- data/lib/avo/base_resource.rb +18 -4
- data/lib/avo/concerns/breadcrumbs.rb +96 -0
- data/lib/avo/concerns/has_html_attributes.rb +14 -16
- data/lib/avo/dashboards/base_dashboard.rb +7 -1
- data/lib/avo/fields/select_field.rb +39 -34
- data/lib/avo/filters/base_filter.rb +8 -1
- data/lib/avo/hosts/visibility_host.rb +2 -0
- data/lib/avo/version.rb +1 -1
- data/public/avo-assets/avo.base.css +4 -4
- metadata +4 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 032f3c926677645e9939a30205693425f4c44c4db874d7e30849c600e13be8a1
|
4
|
+
data.tar.gz: 110d46843bf447a9e1a1215e3d4e17e4c8bf64ca4f88889d72a504c6b9cef06e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: feed3402af12295a712d16f45ced2248c6fcb0663cdad1d4e3b160f9d706e26322ce2966f64f1f6d0fe622f13f2019be8b12d8c16afa7f009ddefe175fd0a7f5
|
7
|
+
data.tar.gz: 28c50de8194d7c649b2c629c988511f1470b00dbaa5d7a60013f743959fecff0b7e97e5c5383548552e412bdea05b9224910c38d681ddbefea25116ade529145
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (2.
|
4
|
+
avo (2.20.0)
|
5
5
|
actionview (>= 6.0)
|
6
6
|
active_link_to
|
7
7
|
activerecord (>= 6.0)
|
8
8
|
addressable
|
9
|
-
breadcrumbs_on_rails
|
10
9
|
docile
|
11
10
|
dry-initializer
|
12
11
|
httparty
|
@@ -127,8 +126,6 @@ GEM
|
|
127
126
|
bootsnap (1.13.0)
|
128
127
|
msgpack (~> 1.2)
|
129
128
|
brakeman (5.3.1)
|
130
|
-
breadcrumbs_on_rails (4.1.0)
|
131
|
-
railties (>= 5.0)
|
132
129
|
builder (3.2.4)
|
133
130
|
bump (0.10.0)
|
134
131
|
bundler-integrity (1.0.9)
|
@@ -238,6 +235,9 @@ GEM
|
|
238
235
|
nokogiri (>= 1.5.9)
|
239
236
|
mail (2.7.1)
|
240
237
|
mini_mime (>= 0.1.1)
|
238
|
+
manifester (0.1.8)
|
239
|
+
rails (>= 6.0)
|
240
|
+
zeitwerk
|
241
241
|
marcel (1.0.2)
|
242
242
|
matrix (0.4.2)
|
243
243
|
meta-tags (2.17.0)
|
@@ -409,7 +409,7 @@ GEM
|
|
409
409
|
tzinfo (2.0.5)
|
410
410
|
concurrent-ruby (~> 1.0)
|
411
411
|
unicode-display_width (2.2.0)
|
412
|
-
view_component (2.
|
412
|
+
view_component (2.77.0)
|
413
413
|
activesupport (>= 5.0.0, < 8.0)
|
414
414
|
concurrent-ruby (~> 1.0)
|
415
415
|
method_source (~> 1.0)
|
@@ -454,7 +454,6 @@ DEPENDENCIES
|
|
454
454
|
aws-sdk-s3
|
455
455
|
bootsnap (>= 1.4.2)
|
456
456
|
brakeman
|
457
|
-
breadcrumbs_on_rails
|
458
457
|
bump
|
459
458
|
bundler-integrity (~> 1.0)
|
460
459
|
byebug
|
@@ -482,6 +481,7 @@ DEPENDENCIES
|
|
482
481
|
jsbundling-rails
|
483
482
|
launchy
|
484
483
|
listen (>= 3.5.1)
|
484
|
+
manifester
|
485
485
|
meta-tags
|
486
486
|
net-smtp
|
487
487
|
pg (>= 0.18, < 2.0)
|
@@ -1,18 +1,18 @@
|
|
1
1
|
<div class="relative flex-1 flex flex-col justify-between h-full"
|
2
2
|
data-controller="dashboard-card"
|
3
3
|
data-dashboard-card-target="card"
|
4
|
-
data-refresh-every="<%=
|
5
|
-
data-card-id="<%=
|
6
|
-
data-card-index="<%=
|
7
|
-
<% if
|
4
|
+
data-refresh-every="<%= card.refresh_every %>"
|
5
|
+
data-card-id="<%= card.id %>"
|
6
|
+
data-card-index="<%= card.index %>">
|
7
|
+
<% if card.class.display_header %>
|
8
8
|
<div class="px-4 pt-4">
|
9
9
|
<div class="flex justify-between items-center min-h-6">
|
10
10
|
<div class="text-base font-bold text-gray-700 leading-none">
|
11
|
-
<%=
|
11
|
+
<%= card.label %>
|
12
12
|
</div>
|
13
13
|
<div data-controller="select">
|
14
|
-
<% if
|
15
|
-
<%= select_tag "#{
|
14
|
+
<% if card.type.in?([:metric, :chartkick, :partial]) && card.ranges.present? %>
|
15
|
+
<%= select_tag "#{card.id}_#{card.index}_range", options_for_select(card.ranges, card.range),
|
16
16
|
class: 'appearance-none inline-flex bg-blue-gray-100 disabled:bg-blue-gray-300 disabled:cursor-not-allowed focus:bg-white text-sm text-blue-gray-700 disabled:text-blue-gray-700 leading-none rounded-md py-px px-2 leading-tight border outline-none outline w-24',
|
17
17
|
data: {
|
18
18
|
target: 'select',
|
@@ -24,18 +24,18 @@
|
|
24
24
|
</div>
|
25
25
|
</div>
|
26
26
|
<% end %>
|
27
|
-
<% if
|
28
|
-
<%= render 'chartkick_card', card:
|
29
|
-
<% elsif
|
30
|
-
<%= render
|
31
|
-
<% elsif
|
27
|
+
<% if card.type == :chartkick %>
|
28
|
+
<%= render 'chartkick_card', card: card %>
|
29
|
+
<% elsif card.type === :partial %>
|
30
|
+
<%= render card.class.partial %>
|
31
|
+
<% elsif card.type === :metric %>
|
32
32
|
<div class="flex-1 flex px-4 pb-4">
|
33
|
-
<%= render 'metric_card', card:
|
33
|
+
<%= render 'metric_card', card: card %>
|
34
34
|
</div>
|
35
35
|
<% end %>
|
36
|
-
<% if
|
36
|
+
<% if card.description.present? %>
|
37
37
|
<%= content_tag :div, class: "absolute inset-auto bottom-0 right-0 mb-4 mr-4",
|
38
|
-
title:
|
38
|
+
title: card.description,
|
39
39
|
data: {
|
40
40
|
target: 'card-description',
|
41
41
|
tippy: 'tooltip',
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Avo::CardComponent < ViewComponent::Base
|
4
|
+
attr_reader :card
|
5
|
+
|
4
6
|
def initialize(card: nil)
|
5
7
|
@card = card
|
6
8
|
|
@@ -8,16 +10,16 @@ class Avo::CardComponent < ViewComponent::Base
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def render?
|
11
|
-
|
13
|
+
card.present?
|
12
14
|
end
|
13
15
|
|
14
16
|
# Initializing the card byt running the query method.
|
15
17
|
# We'll still keep the query block around for compatibility reasons.
|
16
18
|
def init_card
|
17
|
-
if
|
18
|
-
|
19
|
-
elsif
|
20
|
-
|
19
|
+
if card.respond_to? :query
|
20
|
+
card.query
|
21
|
+
elsif card.query_block.present?
|
22
|
+
card.compute_result
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<div class="overflow-hidden flex flex-col">
|
5
5
|
<% if display_breadcrumbs? %>
|
6
6
|
<div class="breadcrumbs truncate mb-2">
|
7
|
-
<%= helpers.
|
7
|
+
<%= helpers.render_avo_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
|
8
8
|
</div>
|
9
9
|
<% end %>
|
10
10
|
<div class="text-2xl tracking-normal font-semibold text-gray-800 truncate items-center flex flex-1" data-target="title">
|
@@ -7,22 +7,28 @@
|
|
7
7
|
<% end %>
|
8
8
|
>
|
9
9
|
<% if item.name.present? %>
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
<%=
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
<% if collapsable %>
|
11
|
+
<div
|
12
|
+
class="flex justify-between cursor-pointer px-10 pr-2 pt-2 pb-0 text-gray-400"
|
13
|
+
data-action="click->menu#triggerCollapse"
|
14
|
+
data-menu-key-param="<%= key %>"
|
15
|
+
>
|
16
|
+
<div class="flex items-center text-xs uppercase font-semibold leading-none">
|
17
|
+
<%= item.name %>
|
18
|
+
</div>
|
19
|
+
<div class="<%= 'rotate-90' if collapsed %>"
|
19
20
|
data-menu-target="svg"
|
20
|
-
data-menu-key-param="<%= key %>"
|
21
21
|
>
|
22
22
|
<%= helpers.svg 'heroicons/outline/chevron-down', class: "h-4 mr-0.5"%>
|
23
23
|
</div>
|
24
|
-
|
25
|
-
|
24
|
+
</div>
|
25
|
+
<% else %>
|
26
|
+
<div class="flex justify-between px-10 pr-2 pt-2 pb-0 text-gray-400">
|
27
|
+
<div class="flex items-center text-xs uppercase font-semibold leading-none">
|
28
|
+
<%= item.name %>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
26
32
|
<% end %>
|
27
33
|
<div class="w-full space-y-1 <%= 'hidden' if collapsed %>" data-menu-target="items">
|
28
34
|
<% items.each do |item| %>
|
@@ -1,14 +1,21 @@
|
|
1
|
-
|
2
|
-
<div class="flex
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
<div class="
|
7
|
-
|
8
|
-
|
1
|
+
<% if collapsable %>
|
2
|
+
<div class="flex justify-between cursor-pointer px-4 pr-2 py-1 text-gray-500"
|
3
|
+
data-action="click->menu#triggerCollapse"
|
4
|
+
data-menu-key-param="<%= key %>"
|
5
|
+
>
|
6
|
+
<div class="flex items-center text-sm uppercase font-semibold leading-none space-x-1">
|
7
|
+
<span class="min-w-[20px]"><%= icon %></span> <span><%= label %></span>
|
8
|
+
</div>
|
9
|
+
<div class="<%= 'rotate-90' if collapsed %>"
|
9
10
|
data-menu-target="svg"
|
10
11
|
>
|
11
12
|
<%= helpers.svg 'heroicons/outline/chevron-down', class: 'h-5'%>
|
12
13
|
</div>
|
13
|
-
|
14
|
-
|
14
|
+
</div>
|
15
|
+
<% else %>
|
16
|
+
<div class="flex justify-between px-4 pr-2 py-1 text-gray-500">
|
17
|
+
<div class="flex items-center text-sm uppercase font-semibold leading-none space-x-1">
|
18
|
+
<span class="min-w-[20px]"><%= icon %></span> <span><%= label %></span>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
@@ -51,7 +51,13 @@ module Avo
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def set_action
|
54
|
-
@action = action_class.new(
|
54
|
+
@action = action_class.new(
|
55
|
+
model: @model,
|
56
|
+
resource: @resource,
|
57
|
+
user: _current_user,
|
58
|
+
view: @view,
|
59
|
+
arguments: @resource.get_action_arguments(action_class)
|
60
|
+
)
|
55
61
|
end
|
56
62
|
|
57
63
|
def action_class
|
@@ -63,8 +69,10 @@ module Avo
|
|
63
69
|
end
|
64
70
|
|
65
71
|
def respond(response)
|
66
|
-
response[:type] ||= :reload
|
67
72
|
messages = get_messages response
|
73
|
+
return keep_modal_open(messages) if response[:keep_modal_open]
|
74
|
+
|
75
|
+
response[:type] ||= :reload
|
68
76
|
|
69
77
|
if response[:type] == :download
|
70
78
|
return send_data response[:path], filename: response[:filename]
|
@@ -73,9 +81,7 @@ module Avo
|
|
73
81
|
respond_to do |format|
|
74
82
|
format.html do
|
75
83
|
# Flash the messages collected from the action
|
76
|
-
messages
|
77
|
-
flash[message[:type]] = message[:body]
|
78
|
-
end
|
84
|
+
flash_messages messages
|
79
85
|
|
80
86
|
if response[:type] == :redirect
|
81
87
|
path = response[:path]
|
@@ -114,5 +120,21 @@ module Avo
|
|
114
120
|
purpose: :select_all
|
115
121
|
)
|
116
122
|
end
|
123
|
+
|
124
|
+
def flash_messages(messages)
|
125
|
+
messages.each do |message|
|
126
|
+
flash[message[:type]] = message[:body]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def keep_modal_open(messages)
|
131
|
+
flash_messages messages
|
132
|
+
|
133
|
+
respond_to do |format|
|
134
|
+
format.turbo_stream do
|
135
|
+
render "keep_modal_open"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
117
139
|
end
|
118
140
|
end
|
@@ -59,7 +59,9 @@ module Avo
|
|
59
59
|
|
60
60
|
# Apply filters to the current query
|
61
61
|
filters_to_be_applied.each do |filter_class, filter_value|
|
62
|
-
@query = filter_class.safe_constantize.new
|
62
|
+
@query = filter_class.safe_constantize.new(
|
63
|
+
arguments: @resource.get_filter_arguments(filter_class)
|
64
|
+
).apply_query request, @query, filter_value
|
63
65
|
end
|
64
66
|
|
65
67
|
extra_pagy_params = {}
|
@@ -224,6 +226,9 @@ module Avo
|
|
224
226
|
@errors = Array.wrap(exception.message)
|
225
227
|
end
|
226
228
|
|
229
|
+
# Add the errors from the model
|
230
|
+
@errors = Array.wrap([@errors, @model.errors.full_messages]).compact
|
231
|
+
|
227
232
|
succeeded
|
228
233
|
end
|
229
234
|
|
@@ -306,8 +311,8 @@ module Avo
|
|
306
311
|
def set_filters
|
307
312
|
@filters = @resource
|
308
313
|
.get_filters
|
309
|
-
.map do |
|
310
|
-
|
314
|
+
.map do |filter|
|
315
|
+
filter[:class].new arguments: filter[:arguments]
|
311
316
|
end
|
312
317
|
.select do |filter|
|
313
318
|
filter.visible_in_view(resource: @resource, parent_model: @parent_model, parent_resource: @parent_resource)
|
@@ -318,7 +323,7 @@ module Avo
|
|
318
323
|
@actions = @resource
|
319
324
|
.get_actions
|
320
325
|
.map do |action|
|
321
|
-
action.new(model: @model, resource: @resource, view: @view)
|
326
|
+
action[:class].new(model: @model, resource: @resource, view: @view, arguments: action[:arguments])
|
322
327
|
end
|
323
328
|
.select do |action|
|
324
329
|
action.visible_in_view(parent_model: @parent_model, parent_resource: @parent_resource)
|
@@ -341,16 +346,16 @@ module Avo
|
|
341
346
|
|
342
347
|
# Go through all filters
|
343
348
|
@resource.get_filters
|
344
|
-
.select do |
|
345
|
-
|
349
|
+
.select do |filter|
|
350
|
+
filter[:class].instance_methods(false).include? :react
|
346
351
|
end
|
347
|
-
.each do |
|
352
|
+
.each do |filter|
|
348
353
|
# Run the react method if it's present
|
349
|
-
reaction =
|
354
|
+
reaction = filter[:class].new(arguments: filter[:arguments]).react
|
350
355
|
|
351
356
|
next if reaction.nil?
|
352
357
|
|
353
|
-
filter_reactions[
|
358
|
+
filter_reactions[filter[:class].to_s] = reaction
|
354
359
|
end
|
355
360
|
|
356
361
|
filter_reactions
|
@@ -360,11 +365,11 @@ module Avo
|
|
360
365
|
def filters_to_be_applied
|
361
366
|
filter_defaults = {}
|
362
367
|
|
363
|
-
@resource.get_filters.each do |
|
364
|
-
filter =
|
368
|
+
@resource.get_filters.each do |filter|
|
369
|
+
filter = filter[:class].new arguments: filter[:arguments]
|
365
370
|
|
366
371
|
unless filter.default.nil?
|
367
|
-
filter_defaults[
|
372
|
+
filter_defaults[filter.class.to_s] = filter.default
|
368
373
|
end
|
369
374
|
end
|
370
375
|
|
@@ -483,7 +488,7 @@ module Avo
|
|
483
488
|
end
|
484
489
|
|
485
490
|
def destroy_fail_message
|
486
|
-
@errors.present? ? @errors.
|
491
|
+
@errors.present? ? @errors.join(". ") : t("avo.failed")
|
487
492
|
end
|
488
493
|
|
489
494
|
def after_destroy_path
|
@@ -46,7 +46,7 @@
|
|
46
46
|
<%= turbo_frame_tag 'actions_show' %>
|
47
47
|
<%= turbo_frame_tag 'attach_modal' %>
|
48
48
|
<%= turbo_frame_tag 'destroy_attachment_form' %>
|
49
|
-
<%= turbo_frame_tag 'alerts', class: "fixed inset-0 bottom-0 flex flex-col space-y-4 items-end justify-right px-4 py-6 sm:p-6 justify-end z-
|
49
|
+
<%= turbo_frame_tag 'alerts', class: "fixed inset-0 bottom-0 flex flex-col space-y-4 items-end justify-right px-4 py-6 sm:p-6 justify-end z-[100] pointer-events-none" do %>
|
50
50
|
<%= render Avo::FlashAlertsComponent.new flashes: flash %>
|
51
51
|
<% # In case we have other general error messages %>
|
52
52
|
<% if @errors.present? %>
|
data/avo.gemspec
CHANGED
@@ -42,7 +42,6 @@ Gem::Specification.new do |spec|
|
|
42
42
|
spec.add_dependency "turbo-rails"
|
43
43
|
spec.add_dependency "addressable"
|
44
44
|
spec.add_dependency "meta-tags"
|
45
|
-
spec.add_dependency "breadcrumbs_on_rails"
|
46
45
|
spec.add_dependency "dry-initializer"
|
47
46
|
spec.add_dependency "docile"
|
48
47
|
spec.add_dependency "inline_svg"
|
data/lib/avo/app.rb
CHANGED
@@ -22,7 +22,10 @@ module Avo
|
|
22
22
|
|
23
23
|
return unless paths.present?
|
24
24
|
|
25
|
-
Rails.
|
25
|
+
pathname = Rails.root.join(*paths)
|
26
|
+
if pathname.directory?
|
27
|
+
Rails.autoloaders.main.eager_load_dir(pathname.to_s)
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
31
|
def boot
|
data/lib/avo/base_action.rb
CHANGED
@@ -19,6 +19,7 @@ module Avo
|
|
19
19
|
attr_accessor :model
|
20
20
|
attr_accessor :resource
|
21
21
|
attr_accessor :user
|
22
|
+
attr_reader :arguments
|
22
23
|
|
23
24
|
delegate :view, to: :class
|
24
25
|
delegate :context, to: ::Avo::App
|
@@ -56,11 +57,12 @@ module Avo
|
|
56
57
|
self.class.to_s.demodulize.underscore.humanize(keep_id_suffix: true)
|
57
58
|
end
|
58
59
|
|
59
|
-
def initialize(model: nil, resource: nil, user: nil, view: nil)
|
60
|
+
def initialize(model: nil, resource: nil, user: nil, view: nil, arguments: {})
|
60
61
|
self.class.model = model if model.present?
|
61
62
|
self.class.resource = resource if resource.present?
|
62
63
|
self.class.user = user if user.present?
|
63
64
|
self.class.view = view if view.present?
|
65
|
+
@arguments = arguments
|
64
66
|
|
65
67
|
self.class.message ||= I18n.t("avo.are_you_sure_you_want_to_run_this_option")
|
66
68
|
self.class.confirm_button_label ||= I18n.t("avo.run")
|
@@ -132,6 +134,7 @@ module Avo
|
|
132
134
|
parent_resource: parent_resource,
|
133
135
|
resource: self.class.resource,
|
134
136
|
view: self.class.view,
|
137
|
+
arguments: arguments
|
135
138
|
).handle
|
136
139
|
end
|
137
140
|
|
@@ -146,6 +149,12 @@ module Avo
|
|
146
149
|
end
|
147
150
|
|
148
151
|
def fail(text)
|
152
|
+
Rails.logger.warn "DEPRECATION WARNING: Action fail method is deprecated in favor of error method and will be removed from Avo version 3.0.0"
|
153
|
+
|
154
|
+
error text
|
155
|
+
end
|
156
|
+
|
157
|
+
def error(text)
|
149
158
|
add_message text, :error
|
150
159
|
|
151
160
|
self
|
@@ -163,6 +172,12 @@ module Avo
|
|
163
172
|
self
|
164
173
|
end
|
165
174
|
|
175
|
+
def keep_modal_open
|
176
|
+
response[:keep_modal_open] = true
|
177
|
+
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
166
181
|
# Add a placeholder silent message from when a user wants to do a redirect action or something similar
|
167
182
|
def silent
|
168
183
|
add_message nil, :silent
|
data/lib/avo/base_card.rb
CHANGED
@@ -15,6 +15,7 @@ module Avo
|
|
15
15
|
|
16
16
|
attr_accessor :dashboard
|
17
17
|
attr_accessor :options
|
18
|
+
attr_accessor :arguments
|
18
19
|
attr_accessor :index
|
19
20
|
attr_accessor :params
|
20
21
|
|
@@ -26,9 +27,10 @@ module Avo
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
29
|
-
def initialize(dashboard:, options: {}, index: 0, cols: nil, rows: nil, label: nil, description: nil, refresh_every: nil)
|
30
|
+
def initialize(dashboard:, options: {}, arguments: {}, index: 0, cols: nil, rows: nil, label: nil, description: nil, refresh_every: nil)
|
30
31
|
@dashboard = dashboard
|
31
32
|
@options = options
|
33
|
+
@arguments = arguments
|
32
34
|
@index = index
|
33
35
|
@cols = cols
|
34
36
|
@rows = rows
|
data/lib/avo/base_resource.rb
CHANGED
@@ -62,16 +62,18 @@ module Avo
|
|
62
62
|
self.grid_loader = grid_collector
|
63
63
|
end
|
64
64
|
|
65
|
-
def action(action_class)
|
65
|
+
def action(action_class, arguments: {})
|
66
66
|
self.actions_loader ||= Avo::Loaders::Loader.new
|
67
67
|
|
68
|
-
|
68
|
+
action = { class: action_class, arguments: arguments }
|
69
|
+
self.actions_loader.use action
|
69
70
|
end
|
70
71
|
|
71
|
-
def filter(filter_class)
|
72
|
+
def filter(filter_class, arguments: {})
|
72
73
|
self.filters_loader ||= Avo::Loaders::Loader.new
|
73
74
|
|
74
|
-
|
75
|
+
filter = { class: filter_class , arguments: arguments }
|
76
|
+
self.filters_loader.use filter
|
75
77
|
end
|
76
78
|
|
77
79
|
# This is the search_query scope
|
@@ -170,12 +172,24 @@ module Avo
|
|
170
172
|
self.class.filters_loader.bag
|
171
173
|
end
|
172
174
|
|
175
|
+
def get_filter_arguments(filter_class)
|
176
|
+
filter = get_filters.find { |filter| filter[:class] == filter_class.constantize }
|
177
|
+
|
178
|
+
filter[:arguments]
|
179
|
+
end
|
180
|
+
|
173
181
|
def get_actions
|
174
182
|
return [] if self.class.actions_loader.blank?
|
175
183
|
|
176
184
|
self.class.actions_loader.bag
|
177
185
|
end
|
178
186
|
|
187
|
+
def get_action_arguments(action_class)
|
188
|
+
action = get_actions.find { |action| action[:class].to_s == action_class.to_s }
|
189
|
+
|
190
|
+
action[:arguments]
|
191
|
+
end
|
192
|
+
|
179
193
|
def default_panel_name
|
180
194
|
return @params[:related_name].capitalize if @params.present? && @params[:related_name].present?
|
181
195
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Avo
|
2
|
+
module Concerns
|
3
|
+
# This is a custom implementation of breadcrumbs largely based on breadcrumbs_on_rails gem
|
4
|
+
# created by Simone Carletti (@weppos) and released on MIT license.
|
5
|
+
#
|
6
|
+
# https://github.com/weppos/breadcrumbs_on_rails
|
7
|
+
#
|
8
|
+
# The reason to use custom implementation is to
|
9
|
+
# * Avoid naming conflicts with other gems adding helpers like `breadcrumbs`
|
10
|
+
# * Reduce number of dependencies
|
11
|
+
module Breadcrumbs
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
included do |base|
|
15
|
+
helper HelperMethods
|
16
|
+
extend ClassMethods
|
17
|
+
helper_method :add_breadcrumb, :avo_breadcrumbs
|
18
|
+
end
|
19
|
+
|
20
|
+
Crumb = Struct.new(:name, :path) unless defined?(Crumb)
|
21
|
+
|
22
|
+
class Builder
|
23
|
+
DEFAULT_SEPARATOR = " » ".freeze unless defined?(DEFAULT_SEPARATOR)
|
24
|
+
|
25
|
+
attr_reader :context, :options
|
26
|
+
|
27
|
+
def initialize(context, options)
|
28
|
+
@context = context
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
def render
|
33
|
+
separator = options.fetch(:separator, DEFAULT_SEPARATOR)
|
34
|
+
breadcrumbs.map(&method(:render_element)).join(separator)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def breadcrumbs
|
40
|
+
context.avo_breadcrumbs
|
41
|
+
end
|
42
|
+
|
43
|
+
def render_element(element)
|
44
|
+
content = element.path.nil? ? compute_name(element) : context.link_to_unless_current(compute_name(element), compute_path(element))
|
45
|
+
options[:tag] ? context.content_tag(options[:tag], content) : ERB::Util.h(content)
|
46
|
+
end
|
47
|
+
|
48
|
+
def compute_name(element)
|
49
|
+
case name = element.name
|
50
|
+
when Symbol
|
51
|
+
context.send(name)
|
52
|
+
when Proc
|
53
|
+
name.call(context)
|
54
|
+
else
|
55
|
+
name.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def compute_path(element)
|
60
|
+
case path = element.path
|
61
|
+
when Symbol
|
62
|
+
context.send(path)
|
63
|
+
when Proc
|
64
|
+
path.call(context)
|
65
|
+
else
|
66
|
+
context.url_for(path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module ClassMethods
|
72
|
+
def add_breadcrumb(name, path = nil)
|
73
|
+
before_action(filter_options) do |controller|
|
74
|
+
controller.send(:add_breadcrumb, name, path)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_breadcrumb(name, path = nil)
|
80
|
+
avo_breadcrumbs << Crumb.new(name, path)
|
81
|
+
end
|
82
|
+
|
83
|
+
def avo_breadcrumbs
|
84
|
+
@avo_breadcrumbs ||= []
|
85
|
+
end
|
86
|
+
|
87
|
+
module HelperMethods
|
88
|
+
def render_avo_breadcrumbs(options = {}, &block)
|
89
|
+
builder = Builder.new(self, options)
|
90
|
+
content = builder.render.html_safe
|
91
|
+
block_given? ? capture(content, &block) : content
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -16,12 +16,14 @@ module Avo
|
|
16
16
|
default_attribute_value name
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
elsif
|
24
|
-
|
19
|
+
parsed = parse_html
|
20
|
+
|
21
|
+
attributes = if parsed.is_a? Hash
|
22
|
+
get_html_from_hash name, element: element, hash: parsed, view: view
|
23
|
+
elsif parsed.is_a? Avo::HTML::Builder
|
24
|
+
get_html_from_block name, element: element, html_builder: parsed, view: view
|
25
|
+
elsif parsed.nil?
|
26
|
+
# Handle empty parsed by returning an empty state
|
25
27
|
default_attribute_value name
|
26
28
|
end
|
27
29
|
|
@@ -30,19 +32,15 @@ module Avo
|
|
30
32
|
|
31
33
|
private
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
# Returns Hash, HTML::Builder, or nil.
|
36
|
+
def parse_html
|
36
37
|
return if @html.nil?
|
37
38
|
|
38
|
-
|
39
|
-
@parsed_html = if @html.is_a? Hash
|
39
|
+
if @html.is_a? Hash
|
40
40
|
@html
|
41
41
|
elsif @html.respond_to? :call
|
42
42
|
Avo::HTML::Builder.parse_block(record: model, resource: resource, &@html)
|
43
43
|
end
|
44
|
-
|
45
|
-
@parsed_html
|
46
44
|
end
|
47
45
|
|
48
46
|
def default_attribute_value(name)
|
@@ -64,7 +62,7 @@ module Avo
|
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
67
|
-
def get_html_from_block(name = nil, element:, view:)
|
65
|
+
def get_html_from_block(name = nil, element:, html_builder:, view:)
|
68
66
|
values = []
|
69
67
|
|
70
68
|
# get view ancestor
|
@@ -83,9 +81,9 @@ module Avo
|
|
83
81
|
merge_values_as(as: values_type, values: values)
|
84
82
|
end
|
85
83
|
|
86
|
-
def get_html_from_hash(name = nil, element:, view:)
|
84
|
+
def get_html_from_hash(name = nil, element:, hash:, view:)
|
87
85
|
# @todo: what if this is not a Hash but a string?
|
88
|
-
|
86
|
+
hash.dig(view, element, name) || {}
|
89
87
|
end
|
90
88
|
|
91
89
|
# Merge the values from all possible locations.
|
@@ -12,7 +12,12 @@ module Avo
|
|
12
12
|
class_attribute :index, default: 0
|
13
13
|
|
14
14
|
class << self
|
15
|
-
def
|
15
|
+
def options_deprecation_message
|
16
|
+
Rails.logger.warn "DEPRECATION WARNING: Card options parameter is deprecated in favor of arguments parameter and will be removed from Avo version 3.0.0"
|
17
|
+
end
|
18
|
+
|
19
|
+
def card(klass, label: nil, description: nil, cols: nil, rows: nil, refresh_every: nil, options: {}, arguments: {})
|
20
|
+
options_deprecation_message if options.present?
|
16
21
|
self.items_holder ||= []
|
17
22
|
|
18
23
|
self.items_holder << klass.new(dashboard: self,
|
@@ -22,6 +27,7 @@ module Avo
|
|
22
27
|
rows: rows,
|
23
28
|
refresh_every: refresh_every,
|
24
29
|
options: options,
|
30
|
+
arguments: arguments,
|
25
31
|
index: index
|
26
32
|
)
|
27
33
|
self.index += 1
|
@@ -3,7 +3,7 @@ module Avo
|
|
3
3
|
class SelectField < BaseField
|
4
4
|
include Avo::Fields::FieldExtensions::HasIncludeBlank
|
5
5
|
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :options_from_args
|
7
7
|
attr_reader :enum
|
8
8
|
attr_reader :display_value
|
9
9
|
|
@@ -12,51 +12,56 @@ module Avo
|
|
12
12
|
|
13
13
|
super(id, **args, &block)
|
14
14
|
|
15
|
-
@
|
16
|
-
|
15
|
+
@options_from_args = if args[:options].is_a? Hash
|
16
|
+
ActiveSupport::HashWithIndifferentAccess.new args[:options]
|
17
|
+
else
|
18
|
+
args[:options]
|
19
|
+
end
|
17
20
|
@enum = args[:enum].present? ? args[:enum] : nil
|
18
21
|
@display_value = args[:display_value].present? ? args[:display_value] : false
|
19
22
|
end
|
20
23
|
|
21
24
|
def options_for_select
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
options.invert
|
32
|
-
else
|
33
|
-
# We need to use the label attribute as the option value because Rails casts it like that
|
34
|
-
options.map { |label, value| [label, label] }.to_h
|
35
|
-
end
|
36
|
-
elsif display_value
|
37
|
-
options.map { |label, value| [value, value] }.to_h
|
38
|
-
else
|
39
|
-
options
|
25
|
+
# If options are array don't need any pre-process
|
26
|
+
return options if options.is_a?(Array)
|
27
|
+
|
28
|
+
# If options are enum we invert the enum if display value, else (see next comment)
|
29
|
+
if enum.present?
|
30
|
+
return enum.invert if display_value
|
31
|
+
|
32
|
+
# We need to use the label attribute as the option value because Rails casts it like that
|
33
|
+
return enum.map { |label, value| [label, label] }.to_h
|
40
34
|
end
|
35
|
+
|
36
|
+
# When code arrive here it means options are Hash
|
37
|
+
# If display_value is true we only need to return the values of the Hash
|
38
|
+
display_value ? options.values : options
|
41
39
|
end
|
42
40
|
|
43
41
|
def label
|
44
|
-
|
45
|
-
|
42
|
+
# If options are array don't need any pre-process
|
43
|
+
return value if options.is_a?(Array)
|
44
|
+
|
45
|
+
# If options are enum and display_value is true we return the Value of that key-value pair, else return key of that key-value pair
|
46
|
+
# WARNING: value here is the DB stored value and not the value of a key-value pair.
|
47
|
+
if enum.present?
|
48
|
+
return enum[value] if display_value
|
49
|
+
return value
|
50
|
+
end
|
51
|
+
|
52
|
+
# When code arrive here it means options are Hash
|
53
|
+
# If display_value is true we only need to return the value stored in DB
|
54
|
+
display_value ? value : options.invert[value]
|
55
|
+
end
|
46
56
|
|
47
|
-
|
57
|
+
private
|
48
58
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
else
|
54
|
-
value
|
55
|
-
end
|
56
|
-
elsif display_value
|
57
|
-
value
|
59
|
+
# Cache options as options given on block or as options received from arguments
|
60
|
+
def options
|
61
|
+
@options ||= if options_from_args.respond_to? :call
|
62
|
+
options_from_args.call model: model, resource: resource, view: view, field: self
|
58
63
|
else
|
59
|
-
|
64
|
+
options_from_args
|
60
65
|
end
|
61
66
|
end
|
62
67
|
end
|
@@ -10,6 +10,8 @@ module Avo
|
|
10
10
|
class_attribute :template, default: "avo/base/select_filter"
|
11
11
|
class_attribute :visible
|
12
12
|
|
13
|
+
attr_reader :arguments
|
14
|
+
|
13
15
|
delegate :params, to: Avo::App
|
14
16
|
|
15
17
|
class << self
|
@@ -24,6 +26,10 @@ module Avo
|
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
29
|
+
def initialize(arguments: {})
|
30
|
+
@arguments = arguments
|
31
|
+
end
|
32
|
+
|
27
33
|
def apply_query(request, query, value)
|
28
34
|
value.stringify_keys! if value.is_a? Hash
|
29
35
|
|
@@ -63,7 +69,8 @@ module Avo
|
|
63
69
|
params: params,
|
64
70
|
parent_model: parent_model,
|
65
71
|
parent_resource: parent_resource,
|
66
|
-
resource: resource
|
72
|
+
resource: resource,
|
73
|
+
arguments: arguments
|
67
74
|
).handle
|
68
75
|
|
69
76
|
end
|
data/lib/avo/version.rb
CHANGED
@@ -6345,14 +6345,14 @@ trix-toolbar .trix-button-group:not(:first-child){
|
|
6345
6345
|
z-index:20
|
6346
6346
|
}
|
6347
6347
|
|
6348
|
-
.z-40{
|
6349
|
-
z-index:40
|
6350
|
-
}
|
6351
|
-
|
6352
6348
|
.z-\[100\]{
|
6353
6349
|
z-index:100
|
6354
6350
|
}
|
6355
6351
|
|
6352
|
+
.z-40{
|
6353
|
+
z-index:40
|
6354
|
+
}
|
6355
|
+
|
6356
6356
|
.z-\[60\]{
|
6357
6357
|
z-index:60
|
6358
6358
|
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: avo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrian Marin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-11-
|
12
|
+
date: 2022-11-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -151,20 +151,6 @@ dependencies:
|
|
151
151
|
- - ">="
|
152
152
|
- !ruby/object:Gem::Version
|
153
153
|
version: '0'
|
154
|
-
- !ruby/object:Gem::Dependency
|
155
|
-
name: breadcrumbs_on_rails
|
156
|
-
requirement: !ruby/object:Gem::Requirement
|
157
|
-
requirements:
|
158
|
-
- - ">="
|
159
|
-
- !ruby/object:Gem::Version
|
160
|
-
version: '0'
|
161
|
-
type: :runtime
|
162
|
-
prerelease: false
|
163
|
-
version_requirements: !ruby/object:Gem::Requirement
|
164
|
-
requirements:
|
165
|
-
- - ">="
|
166
|
-
- !ruby/object:Gem::Version
|
167
|
-
version: '0'
|
168
154
|
- !ruby/object:Gem::Dependency
|
169
155
|
name: dry-initializer
|
170
156
|
requirement: !ruby/object:Gem::Requirement
|
@@ -1648,6 +1634,7 @@ files:
|
|
1648
1634
|
- app/javascript/js/controllers/toggle_panel_controller.js
|
1649
1635
|
- app/javascript/js/helpers/cast_boolean.js
|
1650
1636
|
- app/javascript/js/helpers/debounce_promise.js
|
1637
|
+
- app/views/avo/actions/keep_modal_open.turbo_stream.erb
|
1651
1638
|
- app/views/avo/actions/show.html.erb
|
1652
1639
|
- app/views/avo/associations/new.html.erb
|
1653
1640
|
- app/views/avo/base/_boolean_filter.html.erb
|
@@ -1717,6 +1704,7 @@ files:
|
|
1717
1704
|
- lib/avo/base_card.rb
|
1718
1705
|
- lib/avo/base_resource.rb
|
1719
1706
|
- lib/avo/base_resource_tool.rb
|
1707
|
+
- lib/avo/concerns/breadcrumbs.rb
|
1720
1708
|
- lib/avo/concerns/can_replace_fields.rb
|
1721
1709
|
- lib/avo/concerns/fetches_things.rb
|
1722
1710
|
- lib/avo/concerns/filters_session_handler.rb
|