avo 2.6.0 → 2.7.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 +2 -0
- data/Gemfile.lock +3 -1
- data/app/assets/svgs/heroicons/solid/user-remove.svg +1 -1
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +3 -0
- data/app/components/avo/fields/has_one_field/show_component.html.erb +2 -2
- data/app/components/avo/fields/tags_field/index_component.html.erb +1 -1
- data/app/components/avo/index/resource_controls_component.rb +2 -1
- data/app/components/avo/paginator_component.html.erb +2 -2
- data/app/components/avo/resource_component.rb +7 -0
- data/app/components/avo/sidebar_component.html.erb +1 -1
- data/app/components/avo/views/resource_edit_component.html.erb +5 -0
- data/app/components/avo/views/resource_edit_component.rb +9 -3
- data/app/components/avo/views/resource_show_component.html.erb +7 -2
- data/app/components/avo/views/resource_show_component.rb +5 -2
- data/app/controllers/avo/search_controller.rb +33 -13
- data/app/javascript/js/controllers/search_controller.js +9 -1
- data/app/views/avo/partials/_custom_tools_alert.html.erb +16 -5
- data/app/views/avo/partials/_global_search.html.erb +1 -1
- data/app/views/avo/partials/_resource_search.html.erb +1 -1
- data/app/views/layouts/avo/application.html.erb +1 -1
- data/lib/avo/app.rb +2 -0
- data/lib/avo/base_resource.rb +1 -0
- data/lib/avo/base_resource_tool.rb +34 -0
- data/lib/avo/concerns/has_tools.rb +47 -0
- data/lib/avo/engine.rb +2 -1
- data/lib/avo/hosts/base_host.rb +2 -0
- data/lib/avo/licensing/pro_license.rb +1 -0
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/install_generator.rb +3 -0
- data/lib/generators/avo/resource_tool_generator.rb +40 -0
- data/lib/generators/avo/templates/resource_tools/partial.tt +37 -0
- data/lib/generators/avo/templates/resource_tools/resource_tool.tt +4 -0
- data/public/avo-assets/avo.css +4 -8
- data/public/avo-assets/avo.js +1 -1
- data/public/avo-assets/avo.js.map +2 -2
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c49705a72c31381fe9a133a58b1953d56513fda407157b4c92a17f933424b3e5
|
4
|
+
data.tar.gz: 6680ac6be68f89e9ecdbce81f3ee2ecd18915369509c27914835f824f8da550e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b34915f9540a2e057df9cde8c228077eec22bafee204cb076399a1eb2b3ee36834aff7fb50b9fb57060c3271cb31387d7fcbd27642fca8222a245db04cc70182
|
7
|
+
data.tar.gz: 0a3ce0cad78f3fd6b3cde3534d879e9729569e3e3ac8ea595fd4266c77b6025227bad0154a2f69263c1e8858337906c788d63f635976e2678a94f95d5419d72b
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (2.
|
4
|
+
avo (2.7.0)
|
5
5
|
active_link_to
|
6
6
|
addressable
|
7
7
|
breadcrumbs_on_rails
|
@@ -125,6 +125,7 @@ GEM
|
|
125
125
|
railties (>= 5.0)
|
126
126
|
builder (3.2.4)
|
127
127
|
bump (0.10.0)
|
128
|
+
bundler-integrity (1.0.7)
|
128
129
|
byebug (11.1.3)
|
129
130
|
capybara (3.36.0)
|
130
131
|
addressable
|
@@ -432,6 +433,7 @@ DEPENDENCIES
|
|
432
433
|
bootsnap (>= 1.4.2)
|
433
434
|
breadcrumbs_on_rails
|
434
435
|
bump
|
436
|
+
bundler-integrity (~> 1.0)
|
435
437
|
byebug
|
436
438
|
capybara (= 3.36)
|
437
439
|
countries
|
@@ -1,3 +1,3 @@
|
|
1
|
-
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
2
2
|
<path d="M11 6a3 3 0 11-6 0 3 3 0 016 0zM14 17a6 6 0 00-12 0h12zM13 8a1 1 0 100 2h4a1 1 0 100-2h-4z"/>
|
3
3
|
</svg>
|
@@ -7,6 +7,9 @@
|
|
7
7
|
data-via-association-id="<%= @field.id %>"
|
8
8
|
data-via-reflection-id="<%= @field.model.id %>"
|
9
9
|
data-via-reflection-class="<%= @field.model.class.to_s %>"
|
10
|
+
data-via-parent-resource-id="<%= params[:via_resource_id] %>"
|
11
|
+
data-via-parent-resource-class="<%= params[:via_relation_class] %>"
|
12
|
+
data-via-relation="<%= params[:via_relation] %>"
|
10
13
|
></div>
|
11
14
|
<div class="relative w-full" autocomplete="off">
|
12
15
|
<%= @form.text_field @foreign_key,
|
@@ -3,11 +3,11 @@
|
|
3
3
|
<%= render(Avo::LoadingComponent.new(title: @field.name)) %>
|
4
4
|
</turbo-frame>
|
5
5
|
<% else %>
|
6
|
-
<%= render Avo::PanelComponent.new(title: @field.
|
6
|
+
<%= render Avo::PanelComponent.new(title: @field.name) do |c| %>
|
7
7
|
<% c.tools do %>
|
8
8
|
<% if !@field.readonly && can_attach? %>
|
9
9
|
<%= a_link attach_path, icon: 'heroicons/outline/link', color: :primary, 'data-turbo-frame': 'attach_modal' do %>
|
10
|
-
<%= t('avo.attach_item', item: @field.
|
10
|
+
<%= t('avo.attach_item', item: @field.name) %>
|
11
11
|
<% end %>
|
12
12
|
<% end %>
|
13
13
|
<% end %>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%= index_field_wrapper field: @field do %>
|
1
|
+
<%= index_field_wrapper field: @field, flush: true do %>
|
2
2
|
<div class="flex gap-1 items-center flex-nowrap">
|
3
3
|
<% value.take(3).each do |item| %>
|
4
4
|
<%= render Avo::Fields::TagsField::TagComponent.new(label: label_from_item(item)) %>
|
@@ -41,7 +41,8 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def edit_path
|
44
|
-
|
44
|
+
#Add the `view` param to let Avo know where to redirect back when the user clicks the `Cancel` button.
|
45
|
+
args = {via_view: 'index'}
|
45
46
|
|
46
47
|
if @parent_model.present?
|
47
48
|
args = {
|
@@ -25,9 +25,9 @@
|
|
25
25
|
</div>
|
26
26
|
<% per_page_options.each do |option| %>
|
27
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 %>
|
28
|
+
<%= link_to "Change to #{option} items per page", helpers.related_resources_path(parent_model, parent_model, per_page: option, keep_query_params: true, page: 1), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
|
29
29
|
<% else %>
|
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 %>
|
30
|
+
<%= link_to "Change to #{option} items per page", helpers.resources_path(resource: resource, per_page: option, keep_query_params: true, page: 1), class: 'hidden', 'data-per-page-option': option, 'data-turbo-frame': turbo_frame %>
|
31
31
|
<% end %>
|
32
32
|
<% end %>
|
33
33
|
</div>
|
@@ -1,4 +1,10 @@
|
|
1
1
|
class Avo::ResourceComponent < Avo::BaseComponent
|
2
|
+
attr_reader :fields_by_panel
|
3
|
+
attr_reader :has_one_panels
|
4
|
+
attr_reader :has_many_panels
|
5
|
+
attr_reader :has_as_belongs_to_many_panels
|
6
|
+
attr_reader :resource_tools
|
7
|
+
|
2
8
|
def can_create?
|
3
9
|
return authorize_association_for(:create) if @reflection.present?
|
4
10
|
|
@@ -75,6 +81,7 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
75
81
|
@has_one_panels = []
|
76
82
|
@has_many_panels = []
|
77
83
|
@has_as_belongs_to_many_panels = []
|
84
|
+
@resource_tools = @resource.tools
|
78
85
|
end
|
79
86
|
|
80
87
|
def via_resource?
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div
|
2
|
-
class="fixed z-[60] t-0 application-sidebar hidden lg:flex flex-1 border-r lg:border-none bg-none
|
2
|
+
class="fixed z-[60] t-0 application-sidebar w-64 hidden lg:flex flex-1 border-r lg:border-none bg-none h-[calc(100vh-4rem)] bg-gray-25 lg:bg-transparent <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
|
3
3
|
data-mobile-target="sidebar"
|
4
4
|
>
|
5
5
|
<div class="flex flex-col w-full h-full">
|
@@ -64,5 +64,10 @@
|
|
64
64
|
<%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
|
65
65
|
<% end %>
|
66
66
|
<% end %>
|
67
|
+
<% if resource_tools.present? %>
|
68
|
+
<% resource_tools.each do |tool, index| %>
|
69
|
+
<%= render tool.partial, tool: tool %>
|
70
|
+
<% end %>
|
71
|
+
<% end %>
|
67
72
|
<% end %>
|
68
73
|
</div>
|
@@ -4,8 +4,6 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
|
|
4
4
|
include Avo::ResourcesHelper
|
5
5
|
include Avo::ApplicationHelper
|
6
6
|
|
7
|
-
attr_reader :fields_by_panel, :has_one_panels, :has_many_panels, :has_as_belongs_to_many_panels
|
8
|
-
|
9
7
|
def initialize(resource: nil)
|
10
8
|
@resource = resource
|
11
9
|
|
@@ -15,7 +13,9 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
|
|
15
13
|
def back_path
|
16
14
|
if via_resource?
|
17
15
|
helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
|
18
|
-
|
16
|
+
elsif via_index?
|
17
|
+
helpers.resources_path(resource: @resource)
|
18
|
+
else # via resource show page
|
19
19
|
helpers.resource_path(model: @resource.model, resource: @resource)
|
20
20
|
end
|
21
21
|
end
|
@@ -25,4 +25,10 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
|
|
25
25
|
def can_see_the_save_button?
|
26
26
|
@resource.authorization.authorize_action :edit, raise_exception: false
|
27
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def via_index?
|
32
|
+
params[:via_view] == 'index'
|
33
|
+
end
|
28
34
|
end
|
@@ -15,9 +15,9 @@
|
|
15
15
|
form_class: 'flex flex-col sm:flex-row sm:inline-flex',
|
16
16
|
style: :text,
|
17
17
|
data: {
|
18
|
-
confirm: "Are you sure you want to detach this #{
|
18
|
+
confirm: "Are you sure you want to detach this #{title}."
|
19
19
|
} do %>
|
20
|
-
<%= t('avo.detach_item', item:
|
20
|
+
<%= t('avo.detach_item', item: title).capitalize %>
|
21
21
|
<% end %>
|
22
22
|
<%= render Avo::ActionsComponent.new actions: @actions, resource: @resource %>
|
23
23
|
<% end %>
|
@@ -93,6 +93,11 @@
|
|
93
93
|
<%= render field.component_for_view(:show).new(field: field, resource: @resource, index: index) %>
|
94
94
|
<% end %>
|
95
95
|
<% end %>
|
96
|
+
<% if resource_tools.present? %>
|
97
|
+
<% resource_tools.each do |tool, index| %>
|
98
|
+
<%= render tool.partial, tool: tool %>
|
99
|
+
<% end %>
|
100
|
+
<% end %>
|
96
101
|
<% end %>
|
97
102
|
|
98
103
|
<% if should_display_invalid_fields_errors? %>
|
@@ -4,8 +4,6 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
|
|
4
4
|
include Avo::ResourcesHelper
|
5
5
|
include Avo::ApplicationHelper
|
6
6
|
|
7
|
-
attr_reader :fields_by_panel, :has_one_panels, :has_many_panels, :has_as_belongs_to_many_panels
|
8
|
-
|
9
7
|
def initialize(resource: nil, reflection: nil, parent_model: nil, resource_panel: nil, actions: [])
|
10
8
|
@resource = resource
|
11
9
|
@reflection = reflection
|
@@ -17,6 +15,7 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
|
|
17
15
|
|
18
16
|
def title
|
19
17
|
if @reflection.present?
|
18
|
+
return field.name if has_one_field?
|
20
19
|
reflection_resource.name
|
21
20
|
else
|
22
21
|
@resource.panels.first[:name]
|
@@ -50,4 +49,8 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
|
|
50
49
|
def should_display_invalid_fields_errors?
|
51
50
|
(Rails.env.development? || Rails.env.test?) && @resource.invalid_fields.present?
|
52
51
|
end
|
52
|
+
|
53
|
+
def has_one_field?
|
54
|
+
field.present? and field.class == Avo::Fields::HasOneField
|
55
|
+
end
|
53
56
|
end
|
@@ -46,19 +46,8 @@ module Avo
|
|
46
46
|
def search_resource(resource)
|
47
47
|
query = resource.search_query.call(params: params).limit(8)
|
48
48
|
|
49
|
-
# Figure
|
50
|
-
|
51
|
-
# Fetch the field
|
52
|
-
field = belongs_to_field
|
53
|
-
|
54
|
-
if field.attach_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.attach_scope, query: query, parent: parent).handle
|
60
|
-
end
|
61
|
-
end
|
49
|
+
# Figure out if this is a belongs_to search
|
50
|
+
query = apply_attach_scope query
|
62
51
|
|
63
52
|
results = apply_search_metadata(query, resource)
|
64
53
|
|
@@ -72,6 +61,37 @@ module Avo
|
|
72
61
|
[resource.name.pluralize.downcase, result_object]
|
73
62
|
end
|
74
63
|
|
64
|
+
# Figure out if it's a belongs to search and if it has the attach_scope block present.
|
65
|
+
# If so, set the parent for those edit view and the parent with the grandparent for the new view.
|
66
|
+
def apply_attach_scope(query)
|
67
|
+
return query if params[:via_reflection_class].blank?
|
68
|
+
|
69
|
+
# Fetch the field
|
70
|
+
field = belongs_to_field
|
71
|
+
|
72
|
+
# No need to modify the query if there's no attach_scope present.
|
73
|
+
return query if field.attach_scope.blank?
|
74
|
+
|
75
|
+
# Try to fetch the parent.
|
76
|
+
if params[:via_reflection_id].present?
|
77
|
+
parent = params[:via_reflection_class].safe_constantize.find params[:via_reflection_id]
|
78
|
+
end
|
79
|
+
|
80
|
+
# If the parent is nil it probably means that someone's creating the record so it's not attached yet.
|
81
|
+
# In these scenarios, try to find the grandparent for the new views where the parent is nil
|
82
|
+
# and initialize the parent record with the grandparent attached so the user has the required information
|
83
|
+
# to scope the query.
|
84
|
+
if parent.blank? && params[:via_parent_resource_id].present? && params[:via_parent_resource_class].present? && params[:via_relation].present?
|
85
|
+
grandparent = params[:via_parent_resource_class].safe_constantize.find params[:via_parent_resource_id]
|
86
|
+
parent = params[:via_reflection_class].safe_constantize.new(
|
87
|
+
params[:via_relation] => grandparent
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Add to the query
|
92
|
+
Avo::Hosts::AssociationScopeHost.new(block: belongs_to_field.attach_scope, query: query, parent: parent).handle
|
93
|
+
end
|
94
|
+
|
75
95
|
def apply_search_metadata(models, avo_resource)
|
76
96
|
models.map do |model|
|
77
97
|
resource = avo_resource.dup.hydrate(model: model).hydrate_fields(model: model)
|
@@ -76,6 +76,12 @@ export default class extends Controller {
|
|
76
76
|
via_reflection_class: this.dataset.viaReflectionClass,
|
77
77
|
// eslint-disable-next-line camelcase
|
78
78
|
via_reflection_id: this.dataset.viaReflectionId,
|
79
|
+
// eslint-disable-next-line camelcase
|
80
|
+
via_parent_resource_id: this.dataset.viaParentResourceId,
|
81
|
+
// eslint-disable-next-line camelcase
|
82
|
+
via_parent_resource_class: this.dataset.viaParentResourceClass,
|
83
|
+
// eslint-disable-next-line camelcase
|
84
|
+
via_relation: this.dataset.viaRelation,
|
79
85
|
}
|
80
86
|
}
|
81
87
|
|
@@ -193,7 +199,7 @@ export default class extends Controller {
|
|
193
199
|
Mousetrap.bind(['command+k', 'ctrl+k'], () => this.showSearchPanel())
|
194
200
|
}
|
195
201
|
|
196
|
-
autocomplete({
|
202
|
+
const { destroy } = autocomplete({
|
197
203
|
container: this.autocompleteTarget,
|
198
204
|
placeholder: this.translationKeys.placeholder,
|
199
205
|
translations: {
|
@@ -216,6 +222,8 @@ export default class extends Controller {
|
|
216
222
|
},
|
217
223
|
})
|
218
224
|
|
225
|
+
document.addEventListener('turbo:before-render', destroy)
|
226
|
+
|
219
227
|
// When using search for belongs-to
|
220
228
|
if (this.buttonTarget.dataset.shouldBeDisabled !== 'true') {
|
221
229
|
this.buttonTarget.removeAttribute('disabled')
|
@@ -1,6 +1,17 @@
|
|
1
|
-
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
</
|
1
|
+
<% if @custom_tools_alert_visible %>
|
2
|
+
<div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
|
3
|
+
<a href="https://avohq.io/pricing" target="_blank" class="rounded bg-orange-700 text-white py-2 px-4 text-sm block items-center flex leading-tight">
|
4
|
+
<%= svg "exclamation", class: "h-6 inline mr-2 text-bold flex-shrink-0 mr-1" %> Warning. <%= @custom_tools_alert_visible %> This page will not be visible in a production environment.
|
5
|
+
</a>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
6
8
|
|
9
|
+
<% if Avo::App.error_messages.present? %>
|
10
|
+
<% Avo::App.error_messages.each do |message| %>
|
11
|
+
<div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
|
12
|
+
<a href="https://avohq.io/pricing" target="_blank" class="rounded bg-orange-700 text-white py-2 px-4 text-sm block items-center flex leading-tight">
|
13
|
+
<%= svg "exclamation", class: "h-6 inline mr-2 text-bold flex-shrink-0 mr-1" %> <%= message %>
|
14
|
+
</a>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<div data-controller="search" class="relative flex global-search rounded border border-gray-200 sm:min-w-[16rem]"
|
1
|
+
<div data-controller="search" class="relative flex global-search rounded border border-gray-200 sm:min-w-[16rem]">
|
2
2
|
<div class="flex-1 text-gray-500"
|
3
3
|
data-search-target="autocomplete"
|
4
4
|
data-search-resource="global"
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<div data-controller="search" class="resource-search flex items-center h-full w-full"
|
1
|
+
<div data-controller="search" class="resource-search flex items-center h-full w-full">
|
2
2
|
<div class="w-full"
|
3
3
|
data-search-target="autocomplete"
|
4
4
|
data-search-resource="<%= resource %>"
|
@@ -26,7 +26,7 @@
|
|
26
26
|
<%= render Avo::SidebarComponent.new %>
|
27
27
|
<div class="lg:pl-64 flex-1 flex flex-col min-h-full max-w-full">
|
28
28
|
<div class="content p-4 lg:p-6 flex-1 flex flex-col justify-between items-stretch <%= @container_classes %>">
|
29
|
-
<%= render partial: "avo/partials/custom_tools_alert"
|
29
|
+
<%= render partial: "avo/partials/custom_tools_alert" %>
|
30
30
|
<div class="flex flex-1 flex-col justify-between items-stretch space-y-8">
|
31
31
|
<%= yield %>
|
32
32
|
<%= render partial: "avo/partials/footer" %>
|
data/lib/avo/app.rb
CHANGED
@@ -14,6 +14,7 @@ module Avo
|
|
14
14
|
class_attribute :view_context, default: nil
|
15
15
|
class_attribute :params, default: {}
|
16
16
|
class_attribute :translation_enabled, default: false
|
17
|
+
class_attribute :error_messages, default: []
|
17
18
|
|
18
19
|
class << self
|
19
20
|
def boot
|
@@ -29,6 +30,7 @@ module Avo
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def init(request:, context:, current_user:, root_path:, view_context:, params:)
|
33
|
+
self.error_messages = []
|
32
34
|
self.request = request
|
33
35
|
self.context = context
|
34
36
|
self.current_user = current_user
|
data/lib/avo/base_resource.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Avo
|
2
|
+
class BaseResourceTool
|
3
|
+
include Avo::Fields::FieldExtensions::VisibleInDifferentViews
|
4
|
+
|
5
|
+
class_attribute :name
|
6
|
+
class_attribute :partial
|
7
|
+
|
8
|
+
attr_accessor :params
|
9
|
+
attr_accessor :resource
|
10
|
+
attr_accessor :view
|
11
|
+
|
12
|
+
def initialize(**args)
|
13
|
+
# Set the visibility
|
14
|
+
show_on :show
|
15
|
+
|
16
|
+
show_on args[:show_on] if args[:show_on].present?
|
17
|
+
hide_on args[:hide_on] if args[:hide_on].present?
|
18
|
+
only_on args[:only_on] if args[:only_on].present?
|
19
|
+
except_on args[:except_on] if args[:except_on].present?
|
20
|
+
end
|
21
|
+
|
22
|
+
def hydrate(view: nil)
|
23
|
+
@view = view
|
24
|
+
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def partial
|
29
|
+
return self.class.partial if self.class.partial.present?
|
30
|
+
|
31
|
+
"avo/resource_tools/#{self.class.to_s.underscore}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Avo
|
2
|
+
module Concerns
|
3
|
+
module HasTools
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :tools_holder
|
8
|
+
|
9
|
+
def tools
|
10
|
+
check_license
|
11
|
+
|
12
|
+
return [] if App.license.lacks_with_trial :resource_tools
|
13
|
+
return [] if self.class.tools.blank?
|
14
|
+
|
15
|
+
self.class.tools
|
16
|
+
.map do |tool|
|
17
|
+
tool.hydrate view: view
|
18
|
+
end
|
19
|
+
.select do |field|
|
20
|
+
field.send("show_on_#{view}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class_methods do
|
26
|
+
def tool(klass, **args)
|
27
|
+
self.tools_holder ||= []
|
28
|
+
|
29
|
+
self.tools_holder << klass.new(**args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def tools
|
33
|
+
self.tools_holder
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def check_license
|
40
|
+
if !Rails.env.production? && App.license.lacks(:resource_tools)
|
41
|
+
# Add error message to let the developer know the resource tool will not be available in a production environment.
|
42
|
+
Avo::App.error_messages.push "Warning: Your license is invalid or doesn't support resource tools. The resource tools will not be visible in a production environment."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/avo/engine.rb
CHANGED
@@ -26,7 +26,8 @@ module Avo
|
|
26
26
|
["app", "avo", "actions"],
|
27
27
|
["app", "avo", "resources"],
|
28
28
|
["app", "avo", "dashboards"],
|
29
|
-
["app", "avo", "cards"]
|
29
|
+
["app", "avo", "cards"],
|
30
|
+
["app", "avo", "resource_tools"]
|
30
31
|
].each do |path_params|
|
31
32
|
path = Rails.root.join(*path_params)
|
32
33
|
|
data/lib/avo/hosts/base_host.rb
CHANGED
data/lib/avo/version.rb
CHANGED
@@ -14,6 +14,9 @@ module Generators
|
|
14
14
|
|
15
15
|
template "initializer/avo.tt", "config/initializers/avo.rb"
|
16
16
|
template "locales/avo.en.yml", "config/locales/avo.en.yml"
|
17
|
+
template "locales/avo.nb-NO.yml", "config/locales/avo.nb-NO.yml"
|
18
|
+
template "locales/avo.pt-BR.yml", "config/locales/avo.pt-BR.yml"
|
19
|
+
template "locales/avo.ro.yml", "config/locales/avo.ro.yml"
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module Generators
|
5
|
+
module Avo
|
6
|
+
class ResourceToolGenerator < ::Rails::Generators::NamedBase
|
7
|
+
argument :name, type: :string, required: true
|
8
|
+
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
10
|
+
|
11
|
+
namespace "avo:resource_tool"
|
12
|
+
|
13
|
+
def handle
|
14
|
+
# Add configuration file
|
15
|
+
template "resource_tools/resource_tool.tt", "app/avo/resource_tools/#{file_name}.rb"
|
16
|
+
|
17
|
+
# Add view file
|
18
|
+
template "resource_tools/partial.tt", "app/views/avo/resource_tools/_#{file_name}.html.erb"
|
19
|
+
end
|
20
|
+
|
21
|
+
no_tasks do
|
22
|
+
def file_name
|
23
|
+
name.to_s.underscore
|
24
|
+
end
|
25
|
+
|
26
|
+
def controller_name
|
27
|
+
file_name.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def human_name
|
31
|
+
file_name.humanize
|
32
|
+
end
|
33
|
+
|
34
|
+
def in_code(text)
|
35
|
+
"<code class='p-1 rounded bg-gray-500 text-white text-sm'>#{text}</code>"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<div class="flex flex-col">
|
2
|
+
<%%= render Avo::PanelComponent.new title: "<%= human_name %>" do |c| %>
|
3
|
+
<%% c.tools do %>
|
4
|
+
<%%= a_link('/avo', icon: 'heroicons/solid/academic-cap', style: :primary) do %>
|
5
|
+
Dummy link
|
6
|
+
<%% end %>
|
7
|
+
<%% end %>
|
8
|
+
|
9
|
+
<%% c.body do %>
|
10
|
+
<div class="flex flex-col p-4 min-h-24">
|
11
|
+
<div class="space-y-4">
|
12
|
+
<h3>🪧 This partial is waiting to be updated</h3>
|
13
|
+
|
14
|
+
<p>
|
15
|
+
You can edit this file here <%= in_code "app/views/avo/resource_tools/#{file_name}.html.erb" %>.
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<p>
|
19
|
+
The resource tool configuration file should be here <%= in_code "app/avo/resource_tools/#{file_name}.rb" %>.
|
20
|
+
</p>
|
21
|
+
|
22
|
+
<%%
|
23
|
+
# In this partial you have access to the following variables:
|
24
|
+
# tool
|
25
|
+
# @resource
|
26
|
+
# @resource.model
|
27
|
+
# params
|
28
|
+
# Avo::App.context
|
29
|
+
# current_user
|
30
|
+
%>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<%% end %>
|
34
|
+
<%% end %>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
|
data/public/avo-assets/avo.css
CHANGED
@@ -7277,14 +7277,14 @@ progress[value]::-moz-progress-bar{
|
|
7277
7277
|
width:4rem
|
7278
7278
|
}
|
7279
7279
|
|
7280
|
-
.w-10{
|
7281
|
-
width:2.5rem
|
7282
|
-
}
|
7283
|
-
|
7284
7280
|
.w-64{
|
7285
7281
|
width:16rem
|
7286
7282
|
}
|
7287
7283
|
|
7284
|
+
.w-10{
|
7285
|
+
width:2.5rem
|
7286
|
+
}
|
7287
|
+
|
7288
7288
|
.w-\[1\%\]{
|
7289
7289
|
width:1%
|
7290
7290
|
}
|
@@ -7297,10 +7297,6 @@ progress[value]::-moz-progress-bar{
|
|
7297
7297
|
min-width:300px
|
7298
7298
|
}
|
7299
7299
|
|
7300
|
-
.min-w-\[16rem\]{
|
7301
|
-
min-width:16rem
|
7302
|
-
}
|
7303
|
-
|
7304
7300
|
.min-w-full{
|
7305
7301
|
min-width:100%
|
7306
7302
|
}
|