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.

Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/builds/avo.css +4 -8
  4. data/app/assets/builds/avo.js +1 -1
  5. data/app/assets/builds/avo.js.map +2 -2
  6. data/app/assets/svgs/download-solid-reversed.svg +2 -2
  7. data/app/assets/svgs/heroicons/solid/user-remove.svg +1 -1
  8. data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +1 -0
  9. data/app/components/avo/fields/has_one_field/show_component.html.erb +2 -2
  10. data/app/components/avo/fields/tags_field/index_component.html.erb +1 -1
  11. data/app/components/avo/index/resource_controls_component.rb +2 -1
  12. data/app/components/avo/resource_component.rb +7 -0
  13. data/app/components/avo/views/resource_edit_component.html.erb +5 -0
  14. data/app/components/avo/views/resource_edit_component.rb +9 -3
  15. data/app/components/avo/views/resource_show_component.html.erb +7 -2
  16. data/app/components/avo/views/resource_show_component.rb +5 -2
  17. data/app/controllers/avo/cards_controller.rb +25 -0
  18. data/app/controllers/avo/dashboards_controller.rb +2 -8
  19. data/app/controllers/avo/home_controller.rb +8 -1
  20. data/app/controllers/avo/search_controller.rb +33 -20
  21. data/app/javascript/js/controllers/search_controller.js +2 -0
  22. data/app/views/avo/{dashboards → cards}/_chartkick_card.html.erb +0 -0
  23. data/app/views/avo/{dashboards → cards}/_metric_card.html.erb +0 -0
  24. data/app/views/avo/{dashboards/card.html.erb → cards/show.html.erb} +0 -0
  25. data/app/views/avo/partials/_custom_tools_alert.html.erb +16 -5
  26. data/app/views/layouts/avo/application.html.erb +1 -1
  27. data/config/routes.rb +6 -3
  28. data/lib/avo/app.rb +2 -0
  29. data/lib/avo/base_resource.rb +1 -0
  30. data/lib/avo/base_resource_tool.rb +34 -0
  31. data/lib/avo/concerns/has_tools.rb +47 -0
  32. data/lib/avo/engine.rb +2 -1
  33. data/lib/avo/hosts/association_scope_host.rb +0 -1
  34. data/lib/avo/licensing/pro_license.rb +1 -0
  35. data/lib/avo/version.rb +1 -1
  36. data/lib/generators/avo/install_generator.rb +3 -0
  37. data/lib/generators/avo/resource_tool_generator.rb +40 -0
  38. data/lib/generators/avo/templates/resource_tools/partial.tt +37 -0
  39. data/lib/generators/avo/templates/resource_tools/resource_tool.tt +4 -0
  40. data/public/avo-assets/avo.js +1 -1
  41. data/public/avo-assets/avo.js.map +2 -2
  42. 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" transform="rotate(-180)">
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
- or<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
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.id.capitalize) do |c| %>
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.id).capitalize %>
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
- args = {}
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
- else
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 #{@reflection.name.to_s}."
18
+ confirm: "Are you sure you want to detach this #{title}."
19
19
  } do %>
20
- <%= t('avo.detach_item', item: @reflection.name.to_s).capitalize %>
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[:dashboard_id]
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
- redirect_to Avo.configuration.home_path
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 oute if this is a belongs_to search
50
- if params[:via_reflection_class].present?
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
 
@@ -1,6 +1,17 @@
1
- <div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
2
- <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">
3
- <%= 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.
4
- </a>
5
- </div>
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" if @custom_tools_alert_visible %>
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("/admin")
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
- get "/dashboards/:dashboard_id", to: "dashboards#show"
8
- get "/dashboards/:dashboard_id/cards/:card_id", to: "dashboards#card"
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
@@ -5,6 +5,7 @@ module Avo
5
5
  extend HasContext
6
6
 
7
7
  include ActionView::Helpers::UrlHelper
8
+ include Avo::Concerns::HasTools
8
9
 
9
10
  delegate :view_context, to: "Avo::App"
10
11
  delegate :main_app, to: :view_context
@@ -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
 
@@ -2,7 +2,6 @@ module Avo
2
2
  module Hosts
3
3
  class AssociationScopeHost < BaseHost
4
4
  option :parent
5
- option :grandparent
6
5
  option :query
7
6
  end
8
7
  end
@@ -7,6 +7,7 @@ module Avo
7
7
  :localization,
8
8
  :custom_tools,
9
9
  :custom_fields,
10
+ :resource_tools,
10
11
  :global_search,
11
12
  :enhanced_search_results,
12
13
  :searchable_associations,
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.6.1.pre.1" unless const_defined?(:VERSION)
2
+ VERSION = "2.7.1.pre.1" unless const_defined?(:VERSION)
3
3
  end
@@ -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
+
@@ -0,0 +1,4 @@
1
+ class <%= class_name %> < Avo::BaseResourceTool
2
+ self.name = "<%= human_name %>"
3
+ # self.partial = "avo/resource_tools/<%= file_name %>"
4
+ end