avo 2.6.1.pre.1 → 2.7.1.pre.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/assets/builds/avo.css +4 -8
- data/app/assets/builds/avo.js +1 -1
- data/app/assets/builds/avo.js.map +2 -2
- data/app/assets/svgs/download-solid-reversed.svg +2 -2
- data/app/assets/svgs/heroicons/solid/user-remove.svg +1 -1
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +1 -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/resource_component.rb +7 -0
- 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/cards_controller.rb +25 -0
- data/app/controllers/avo/dashboards_controller.rb +2 -8
- data/app/controllers/avo/home_controller.rb +8 -1
- data/app/controllers/avo/search_controller.rb +33 -20
- data/app/javascript/js/controllers/search_controller.js +2 -0
- data/app/views/avo/{dashboards → cards}/_chartkick_card.html.erb +0 -0
- data/app/views/avo/{dashboards → cards}/_metric_card.html.erb +0 -0
- data/app/views/avo/{dashboards/card.html.erb → cards/show.html.erb} +0 -0
- data/app/views/avo/partials/_custom_tools_alert.html.erb +16 -5
- data/app/views/layouts/avo/application.html.erb +1 -1
- data/config/routes.rb +6 -3
- 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/association_scope_host.rb +0 -1
- 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.js +1 -1
- data/public/avo-assets/avo.js.map +2 -2
- metadata +11 -5
@@ -1,3 +1,3 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
|
2
|
-
<path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
2
|
+
<path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" transform="rotate(-180 10 10)" />
|
3
3
|
</svg>
|
@@ -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>
|
@@ -9,6 +9,7 @@
|
|
9
9
|
data-via-reflection-class="<%= @field.model.class.to_s %>"
|
10
10
|
data-via-parent-resource-id="<%= params[:via_resource_id] %>"
|
11
11
|
data-via-parent-resource-class="<%= params[:via_relation_class] %>"
|
12
|
+
data-via-relation="<%= params[:via_relation] %>"
|
12
13
|
></div>
|
13
14
|
<div class="relative w-full" autocomplete="off">
|
14
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 = {
|
@@ -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?
|
@@ -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
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_dependency "avo/application_controller"
|
2
|
+
|
3
|
+
module Avo
|
4
|
+
class CardsController < ApplicationController
|
5
|
+
before_action :set_dashboard, only: :show
|
6
|
+
before_action :set_card, only: :show
|
7
|
+
|
8
|
+
def show
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def set_dashboard
|
14
|
+
@dashboard = Avo::App.get_dashboard_by_id params[:dashboard_id]
|
15
|
+
|
16
|
+
raise ActionController::RoutingError.new("Not Found") if @dashboard.nil? || @dashboard.is_hidden?
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_card
|
20
|
+
@card = @dashboard.item_at_index(params[:index].to_i).tap do |card|
|
21
|
+
card.hydrate(dashboard: @dashboard, params: params)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,21 +2,15 @@ require_dependency "avo/application_controller"
|
|
2
2
|
|
3
3
|
module Avo
|
4
4
|
class DashboardsController < ApplicationController
|
5
|
-
before_action :set_dashboard
|
5
|
+
before_action :set_dashboard, only: :show
|
6
6
|
|
7
7
|
def show
|
8
8
|
end
|
9
9
|
|
10
|
-
def card
|
11
|
-
@card = @dashboard.item_at_index(params[:index].to_i).tap do |card|
|
12
|
-
card.hydrate(dashboard: @dashboard, params: params)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
10
|
private
|
17
11
|
|
18
12
|
def set_dashboard
|
19
|
-
@dashboard = Avo::App.get_dashboard_by_id params[:
|
13
|
+
@dashboard = Avo::App.get_dashboard_by_id params[:id]
|
20
14
|
|
21
15
|
raise ActionController::RoutingError.new("Not Found") if @dashboard.nil? || @dashboard.is_hidden?
|
22
16
|
end
|
@@ -4,7 +4,14 @@ module Avo
|
|
4
4
|
class HomeController < ApplicationController
|
5
5
|
def index
|
6
6
|
if Avo.configuration.home_path.present?
|
7
|
-
|
7
|
+
# If the home_path is a block run it, if not, just use it
|
8
|
+
computed_path = if Avo.configuration.home_path.respond_to? :call
|
9
|
+
instance_exec(&Avo.configuration.home_path)
|
10
|
+
else
|
11
|
+
Avo.configuration.home_path
|
12
|
+
end
|
13
|
+
|
14
|
+
redirect_to computed_path
|
8
15
|
elsif !Rails.env.development?
|
9
16
|
@page_title = "Get started"
|
10
17
|
resource = Avo::App.resources.min_by { |resource| resource.model_key }
|
@@ -46,26 +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
|
-
# Try to fetch the parent.
|
56
|
-
if params[:via_reflection_id].present?
|
57
|
-
parent = params[:via_reflection_class].safe_constantize.find params[:via_reflection_id]
|
58
|
-
end
|
59
|
-
|
60
|
-
# Try to fetch the grandparent for the new views where the parent is nil.
|
61
|
-
if params[:via_parent_resource_id].present? && params[:via_parent_resource_class].present?
|
62
|
-
grandparent = params[:via_parent_resource_class].safe_constantize.find params[:via_parent_resource_id]
|
63
|
-
end
|
64
|
-
|
65
|
-
# Add to the query
|
66
|
-
query = Avo::Hosts::AssociationScopeHost.new(block: belongs_to_field.attach_scope, query: query, parent: parent, grandparent: grandparent).handle
|
67
|
-
end
|
68
|
-
end
|
49
|
+
# Figure out if this is a belongs_to search
|
50
|
+
query = apply_attach_scope query
|
69
51
|
|
70
52
|
results = apply_search_metadata(query, resource)
|
71
53
|
|
@@ -79,6 +61,37 @@ module Avo
|
|
79
61
|
[resource.name.pluralize.downcase, result_object]
|
80
62
|
end
|
81
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
|
+
|
82
95
|
def apply_search_metadata(models, avo_resource)
|
83
96
|
models.map do |model|
|
84
97
|
resource = avo_resource.dup.hydrate(model: model).hydrate_fields(model: model)
|
@@ -80,6 +80,8 @@ export default class extends Controller {
|
|
80
80
|
via_parent_resource_id: this.dataset.viaParentResourceId,
|
81
81
|
// eslint-disable-next-line camelcase
|
82
82
|
via_parent_resource_class: this.dataset.viaParentResourceClass,
|
83
|
+
// eslint-disable-next-line camelcase
|
84
|
+
via_relation: this.dataset.viaRelation,
|
83
85
|
}
|
84
86
|
}
|
85
87
|
|
File without changes
|
File without changes
|
File without changes
|
@@ -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 %>
|
@@ -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/config/routes.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
Avo::Engine.routes.draw do
|
2
2
|
root "home#index"
|
3
3
|
|
4
|
-
get "resources", to: redirect(
|
4
|
+
get "resources", to: redirect(Avo.configuration.root_path)
|
5
|
+
get "dashboards", to: redirect(Avo.configuration.root_path)
|
6
|
+
|
5
7
|
post "/rails/active_storage/direct_uploads", to: "/active_storage/direct_uploads#create"
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
resources :dashboards do
|
10
|
+
resources :cards
|
11
|
+
end
|
9
12
|
|
10
13
|
scope "avo_api", as: "avo_api" do
|
11
14
|
get "/search", to: "search#index"
|
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/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
|
+
|