avo 1.0.5 → 1.2.4

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -1
  3. data/Gemfile.lock +11 -3
  4. data/app/components/avo/index/grid_cover_empty_state_component.html.erb +3 -0
  5. data/app/components/avo/index/grid_cover_empty_state_component.rb +4 -0
  6. data/app/components/avo/index/grid_item_component.html.erb +4 -4
  7. data/app/components/avo/modal_component.rb +2 -1
  8. data/app/components/avo/panel_component.html.erb +7 -2
  9. data/app/components/avo/panel_component.rb +4 -1
  10. data/app/components/avo/show/fields/has_one_field_component.html.erb +3 -3
  11. data/app/components/avo/views/resource_edit_component.html.erb +3 -3
  12. data/app/components/avo/views/resource_index_component.html.erb +4 -4
  13. data/app/components/avo/views/resource_new_component.html.erb +3 -3
  14. data/app/components/avo/views/resource_show_component.html.erb +2 -2
  15. data/app/controllers/avo/application_controller.rb +12 -8
  16. data/app/controllers/avo/base_controller.rb +16 -0
  17. data/app/controllers/avo/home_controller.rb +2 -0
  18. data/app/helpers/avo/application_helper.rb +0 -30
  19. data/app/packs/entrypoints/application.css +1 -0
  20. data/app/packs/stylesheets/breadcrumbs.css +12 -0
  21. data/app/packs/svgs/chevron-right.svg +3 -0
  22. data/app/views/avo/actions/show.html.erb +2 -2
  23. data/app/views/avo/home/_actions.html.erb +5 -5
  24. data/app/views/avo/home/_docs.html.erb +1 -1
  25. data/app/views/avo/home/_filters.html.erb +4 -4
  26. data/app/views/avo/home/_resources.html.erb +15 -2
  27. data/app/views/avo/home/index.html.erb +4 -4
  28. data/{lib/generators/avo/templates → app/views/avo}/partials/_scripts.html.erb +0 -0
  29. data/app/views/avo/relations/new.html.erb +2 -2
  30. data/app/views/avo/sidebar/_sidebar.html.erb +12 -3
  31. data/app/views/layouts/avo/application.html.erb +5 -4
  32. data/avo.gemspec +2 -0
  33. data/lib/avo/app.rb +33 -10
  34. data/lib/avo/base_resource.rb +1 -0
  35. data/lib/avo/configuration.rb +11 -1
  36. data/lib/avo/grid_collector.rb +1 -1
  37. data/lib/avo/version.rb +1 -1
  38. data/lib/generators/avo/eject_generator.rb +55 -0
  39. data/lib/generators/avo/templates/tool/controller.tt +2 -0
  40. data/lib/generators/avo/templates/tool/sidebar_item.tt +1 -0
  41. data/lib/generators/avo/templates/tool/view.tt +27 -0
  42. data/lib/generators/avo/tool_generator.rb +62 -0
  43. data/public/avo-packs/css/{application-9d115b7e.css → application-43cdc2a7.css} +44 -7
  44. data/public/avo-packs/css/application-43cdc2a7.css.br +0 -0
  45. data/public/avo-packs/css/application-43cdc2a7.css.gz +0 -0
  46. data/public/avo-packs/css/application-43cdc2a7.css.map +1 -0
  47. data/public/avo-packs/css/application-43cdc2a7.css.map.br +0 -0
  48. data/public/avo-packs/css/application-43cdc2a7.css.map.gz +0 -0
  49. data/public/avo-packs/manifest.json +7 -7
  50. metadata +46 -13
  51. data/lib/generators/avo/partials_generator.rb +0 -14
  52. data/lib/generators/avo/templates/partials/_footer.html.erb +0 -3
  53. data/lib/generators/avo/templates/partials/_header.html.erb +0 -1
  54. data/lib/generators/avo/templates/partials/_logo.html.erb +0 -1
  55. data/public/avo-packs/css/application-9d115b7e.css.br +0 -0
  56. data/public/avo-packs/css/application-9d115b7e.css.gz +0 -0
  57. data/public/avo-packs/css/application-9d115b7e.css.map +0 -1
  58. data/public/avo-packs/css/application-9d115b7e.css.map.br +0 -0
  59. data/public/avo-packs/css/application-9d115b7e.css.map.gz +0 -0
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
2
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
3
+ </svg>
@@ -9,7 +9,7 @@
9
9
  >
10
10
  <%= form_with model: @model, scope: 'fields', url: "/avo/resources/#{@resource.model_class.model_name.route_key}/actions/#{@action.param_id}", data: {'turbo-frame': '_top', 'action-target': 'form'} do |form| %>
11
11
  <%= render Avo::ModalComponent.new do |c| %>
12
- <% c.with :heading do %>
12
+ <% c.heading do %>
13
13
  <%= @action.name %>
14
14
  <% end %>
15
15
 
@@ -25,7 +25,7 @@
25
25
  <% end %>
26
26
  <% end %>
27
27
 
28
- <% c.with :controls do %>
28
+ <% c.controls do %>
29
29
  <%= a_button @action.cancel_button_label, 'data-action': 'click->modal#close', size: :sm %>
30
30
  <%= a_button @action.confirm_button_label, type: :submit, color: :green, size: :sm %>
31
31
  <% end %>
@@ -1,16 +1,16 @@
1
1
  <div>
2
2
  <h3>Actions</h3>
3
3
 
4
- <div class="mt-2" data-controller="hidden-input">
4
+ <div class="mt-2">
5
5
  Avo actions allow you to perform specific tasks on one or more of your records. For example, you might want to mark a user as inactive and optionally send a message that may be customized by the person that wants to run the action.
6
6
 
7
- <strong class="cursor-pointer block mt-2" data-action="click->hidden-input#showContent">Generate action 👇</strong>
8
-
9
- <div class="mt-2 hidden" data-hidden-input-target="content">
7
+ <div class="mt-2">
10
8
  <code class="text-lg">Toggle published</code>
11
- <div>
9
+ <div class="mt-1">
12
10
  <code class="p-1 rounded bg-light-blue-500 text-white">bin/rails generate avo:action toggle_published</code>
13
11
  </div>
14
12
  </div>
13
+
14
+ <a href="https://docs.avohq.io/1.0/actions.html" target="_blank" title="Avo Actions documentation" class="text-bold cursor-pointer block mt-2">Actions in the docs 👉</a>
15
15
  </div>
16
16
  </div>
@@ -4,6 +4,6 @@
4
4
  <div class="mt-2">
5
5
  We take pride in our documentation. Please check it out to see what you can build with Avo
6
6
 
7
- <a href="https://docs.avohq.io" class="text-bold cursor-pointer block mt-2">Check out the docs 👉</a>
7
+ <a href="https://docs.avohq.io" target="_blank" title="Avo documentation" class="text-bold cursor-pointer block mt-2">Check out the docs 👉</a>
8
8
  </div>
9
9
  </div>
@@ -4,13 +4,13 @@
4
4
  <div class="mt-2" data-controller="hidden-input">
5
5
  Filters allow you to better scope the index queries for records you are looking for.
6
6
 
7
- <strong class="cursor-pointer block mt-2" data-action="click->hidden-input#showContent">Generate filter 👇</strong>
8
-
9
- <div class="mt-2 hidden" data-hidden-input-target="content">
7
+ <div class="mt-2">
10
8
  <code class="text-lg">Published filter</code>
11
- <div>
9
+ <div class="mt-1">
12
10
  <code class="p-1 rounded bg-light-blue-500 text-white">bin/rails generate avo:filter published_filter</code>
13
11
  </div>
14
12
  </div>
13
+
14
+ <a href="https://docs.avohq.io/1.0/filters.html" target="_blank" title="Avo Filters documentation" class="text-bold cursor-pointer block mt-2">Filters in the docs 👉</a>
15
15
  </div>
16
16
  </div>
@@ -1,5 +1,9 @@
1
1
  <%
2
- models = ApplicationRecord.descendants
2
+ models = begin
3
+ ApplicationRecord.descendants
4
+ rescue => exception
5
+ []
6
+ end
3
7
  %>
4
8
  <div>
5
9
  <h3>Resources</h3>
@@ -10,7 +14,7 @@
10
14
  <div class="mt-2" data-controller="hidden-input">
11
15
  It seems you already have <%= models.count %> <%= 'model'.pluralize models.count %> in your app. You can generate resources for each one with the following commands.
12
16
 
13
- <strong class="cursor-pointer block mt-2" data-action="click->hidden-input#showContent">Generate resources 👇</strong>
17
+ <strong class="cursor-pointer block mt-2" data-action="click->hidden-input#showContent">Generate resources 👇 (click to reveal)</strong>
14
18
 
15
19
  <div class="mt-2 hidden" data-hidden-input-target="content">
16
20
  <% models.each do |model| %>
@@ -22,7 +26,16 @@
22
26
  </div>
23
27
  </div>
24
28
  <% else %>
29
+ <div class="mt-2" data-controller="hidden-input">
30
+ You may generate a resource using the following command:
31
+ <br>
25
32
 
33
+ <code class="p-1 rounded bg-light-blue-500 text-white">bin/rails generate avo:resource post</code>
34
+ </div>
26
35
  <% end %>
36
+
37
+ <p>
38
+ <a href="https://docs.avohq.io/1.0/resources.html" target="_blank" title="Avo Resources documentation">Docs</a>
39
+ </p>
27
40
  </div>
28
41
 
@@ -1,10 +1,10 @@
1
1
  <div class="flex flex-col">
2
- <%= panel title: 'Welcome to Avo' do |c| %>
3
- <% c.with :tools do %>
2
+ <%= render Avo::PanelComponent.new title: 'Welcome to Avo' do |c| %>
3
+ <% c.tools do %>
4
4
  <div class="text-sm italic">This page is visible only in development. It will be hidden in other environments.</div>
5
5
  <% end %>
6
6
 
7
- <% c.with :body do %>
7
+ <% c.body do %>
8
8
  <div class="flex flex-col justify-between py-6 min-h-24">
9
9
  <div class="px-6 space-y-4">
10
10
  <h3>About</h3>
@@ -27,7 +27,7 @@
27
27
  <li><span class="mr-2">👍</span> Grid view</li>
28
28
  <li><span class="mr-2">👍</span> Authorization</li>
29
29
  <li><span class="mr-2">👍</span> Localization</li>
30
- <li><span class="mr-2">⏳</span> Custom tools</li>
30
+ <li><span class="mr-2">👍</span> Custom tools</li>
31
31
  <li><span class="mr-2">⏳</span> Custom fields</li>
32
32
  <li><span class="mr-2">⏳</span> Search</li>
33
33
  <li><span class="mr-2">🕐</span> Dashboards</li>
@@ -5,7 +5,7 @@
5
5
  'turbo-frame': '_top'
6
6
  } do |form| %>
7
7
  <%= render Avo::ModalComponent.new do |c| %>
8
- <% c.with :heading do %>
8
+ <% c.heading do %>
9
9
  <%= t 'avo.choose_item', item: params[:related_name].downcase %>
10
10
  <% end %>
11
11
 
@@ -24,7 +24,7 @@
24
24
 
25
25
  <%#= form.hidden_field :resource_ids, value: params[:resource_ids], 'data-action-target': 'resourceIds' %>
26
26
 
27
- <% c.with :controls do %>
27
+ <% c.controls do %>
28
28
  <%= a_button t('avo.cancel'), 'data-action': 'click->modal#close', size: :sm %>
29
29
  <%= a_button t('avo.attach'), type: :submit, color: :green, size: :sm %>
30
30
  <% end %>
@@ -1,18 +1,27 @@
1
1
  <div class="application-sidebar flex h-full bg-white text-white w-56 z-50 border-r border-gray-300">
2
2
  <div class="flex flex-col w-full">
3
3
  <%= link_to root_path, class: 'logo-placeholder h-16 bg-white p-2 flex justify-center' do %>
4
- <%= render_logo %>
4
+ <%= render partial: "avo/partials/logo" %>
5
5
  <% end %>
6
6
 
7
7
  <div class="flex-1 flex flex-col justify-between">
8
8
  <div class="tools py-4">
9
- <%= render Avo::NavigationLinkComponent.new label: 'Get started', path: '/avo', active: :exclusive if Rails.env.development? %>
9
+ <%= render Avo::NavigationLinkComponent.new label: 'Get started', path: Avo.configuration.root_path, active: :exclusive if Rails.env.development? %>
10
10
  <%= render Avo::NavigationHeadingComponent.new label: t('avo.resources') %>
11
11
 
12
12
  <div class="w-full">
13
- <% Avo::App.get_navigation_items(_current_user).each do |resource| %>
13
+ <% Avo::App.resources_navigation(_current_user).each do |resource| %>
14
14
  <%= render Avo::NavigationLinkComponent.new label: resource.name.pluralize.humanize, path: resources_path(resource.model_class) %>
15
15
  <% end %>
16
+
17
+ <% sidebar_partials = Avo::App.get_sidebar_partials %>
18
+ <% if sidebar_partials.present? %>
19
+ <%= render Avo::NavigationHeadingComponent.new label: t('avo.tools') %>
20
+
21
+ <% sidebar_partials.each do |partial| %>
22
+ <%= render partial: "/avo/sidebar/items/#{partial}" %>
23
+ <% end %>
24
+ <% end %>
16
25
  </div>
17
26
 
18
27
  </div>
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title>Avo Hotwire</title>
4
+ <%= display_meta_tags site: Avo.configuration.app_name, reverse: true, separator: "—" %>
5
5
 
6
6
  <%= csrf_meta_tags %>
7
7
  <%= csp_meta_tag %>
@@ -20,7 +20,7 @@
20
20
  <div class="flex-1 flex flex-col h-full overflow-auto">
21
21
  <div class="relative bg-white p-2 shadow-md h-16 w-full flex flex-shrink-0 items-center z-50" v-if="layout !== 'blank'">
22
22
  <div class="ml-6">
23
- <%= render_header %>
23
+ <%= render partial: "avo/partials/header" %>
24
24
  </div>
25
25
  <div class="flex-1 flex justify-center">
26
26
  <div class="w-64">
@@ -31,7 +31,7 @@
31
31
 
32
32
  <div class="content p-8 flex-1 flex flex-col justify-between items-stretch <%= @container_classes %>">
33
33
  <%= yield %>
34
- <%= render_footer %>
34
+ <%= render partial: "avo/partials/footer" %>
35
35
  </div>
36
36
  </div>
37
37
  </div>
@@ -44,6 +44,7 @@
44
44
  <% end %>
45
45
  </div>
46
46
 
47
- <%= render_scripts %>
47
+ <%= render partial: "avo/partials/scripts" %>
48
+ <!-- Avo version: <%= Avo::VERSION %> -->
48
49
  </body>
49
50
  </html>
data/avo.gemspec CHANGED
@@ -43,4 +43,6 @@ Gem::Specification.new do |spec|
43
43
  spec.add_dependency "view_component"
44
44
  spec.add_dependency "hotwire-rails"
45
45
  spec.add_dependency "addressable"
46
+ spec.add_dependency "meta-tags"
47
+ spec.add_dependency "breadcrumbs_on_rails"
46
48
  end
data/lib/avo/app.rb CHANGED
@@ -27,7 +27,11 @@ module Avo
27
27
  self.context = context
28
28
 
29
29
  # Set the current host for ActiveStorage
30
- ActiveStorage::Current.host = request.base_url
30
+ begin
31
+ ActiveStorage::Current.host = request.base_url
32
+ rescue => exception
33
+ Rails.logger.debug "[Avo] Failed to set ActiveStorage::Current.host, #{exception.inspect}"
34
+ end
31
35
 
32
36
  init_resources
33
37
 
@@ -129,10 +133,28 @@ module Avo
129
133
  .sort_by { |r| r.name }
130
134
  end
131
135
 
132
- def get_navigation_items(user = nil)
136
+ def resources_navigation(user = nil)
133
137
  get_available_resources(user).select do |resource|
134
138
  resource.model_class.present?
135
139
  end
140
+ .select do |resource|
141
+ resource.visible_on_sidebar
142
+ end
143
+ end
144
+
145
+ # Insert any partials that we find in app/views/avo/sidebar/items.
146
+ def get_sidebar_partials
147
+ Dir.glob(Rails.root.join("app", "views", "avo", "sidebar", "items", "*.html.erb"))
148
+ .map do |path|
149
+ File.basename path
150
+ end
151
+ .map do |filename|
152
+ # remove the leading underscore (_)
153
+ filename[0] = ""
154
+ # remove the extension
155
+ filename.gsub!('.html.erb', '')
156
+ filename
157
+ end
136
158
  end
137
159
 
138
160
  def draw_routes
@@ -144,16 +166,17 @@ module Avo
144
166
  .select do |resource|
145
167
  resource != :BaseResource
146
168
  end
169
+ .select do |resource|
170
+ resource.is_a? Class
171
+ end
147
172
  .map do |resource|
148
- if resource.is_a? Class
149
- route_key = if resource.model_class.present?
150
- resource.model_class.model_name.route_key
151
- else
152
- resource.to_s.underscore.gsub("_resource", "").downcase.pluralize.to_sym
153
- end
154
-
155
- resources route_key
173
+ route_key = if resource.model_class.present?
174
+ resource.model_class.model_name.route_key
175
+ else
176
+ resource.to_s.underscore.gsub("_resource", "").downcase.pluralize.to_sym
156
177
  end
178
+
179
+ resources route_key
157
180
  end
158
181
  end
159
182
  end
@@ -21,6 +21,7 @@ module Avo
21
21
  class_attribute :filters_loader
22
22
  class_attribute :fields
23
23
  class_attribute :grid_loader
24
+ class_attribute :visible_on_sidebar, default: true
24
25
 
25
26
  class << self
26
27
  def grid(&block)
@@ -19,10 +19,12 @@ module Avo
19
19
  attr_accessor :full_width_index_view
20
20
  attr_accessor :cache_resources_on_index_view
21
21
  attr_accessor :context
22
+ attr_accessor :display_breadcrumbs
23
+ attr_accessor :initial_breadcrumbs
22
24
 
23
25
  def initialize
24
26
  @root_path = "/avo"
25
- @app_name = Rails.application.class.to_s.split("::").first.underscore.humanize(keep_id_suffix: true)
27
+ @app_name = ::Rails.application.class.to_s.split("::").first.underscore.humanize(keep_id_suffix: true)
26
28
  @timezone = "UTC"
27
29
  @per_page = 24
28
30
  @per_page_steps = [12, 24, 48, 72]
@@ -48,6 +50,10 @@ module Avo
48
50
  @full_width_index_view = false
49
51
  @cache_resources_on_index_view = Avo::PACKED
50
52
  @context = proc {}
53
+ @initial_breadcrumbs = proc {
54
+ add_breadcrumb I18n.t("avo.home").humanize, avo.root_path
55
+ }
56
+ @display_breadcrumbs = true
51
57
  end
52
58
 
53
59
  def locale_tag
@@ -71,6 +77,10 @@ module Avo
71
77
  def set_context(&block)
72
78
  @context = block if block.present?
73
79
  end
80
+
81
+ def set_initial_breadcrumbs(&block)
82
+ @initial_breadcrumbs = block if block.present?
83
+ end
74
84
  end
75
85
 
76
86
  def self.configuration
@@ -27,7 +27,7 @@ module Avo
27
27
  def hydrate(model:, view:, resource:)
28
28
  cover_field.hydrate(model: model, view: view, resource: resource) if cover_field.present?
29
29
  title_field.hydrate(model: model, view: view, resource: resource) if title_field.present?
30
- body_field.hydrate(model: model, view: view, resource: resource) if title_field.present?
30
+ body_field.hydrate(model: model, view: view, resource: resource) if body_field.present?
31
31
 
32
32
  self
33
33
  end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "1.0.5"
2
+ VERSION = "1.2.4"
3
3
  end
@@ -0,0 +1,55 @@
1
+ require "rails/generators"
2
+ require "fileutils"
3
+
4
+ module Generators
5
+ module Avo
6
+ class EjectGenerator < ::Rails::Generators::Base
7
+ argument :filename, type: :string, required: true
8
+
9
+ source_root ::Avo::Engine.root
10
+
11
+ namespace "avo:eject"
12
+
13
+ TEMPLATES = {
14
+ sidebar: "app/views/avo/sidebar/_sidebar.html.erb",
15
+ logo: "app/views/avo/partials/logo.html.erb",
16
+ header: "app/views/avo/partials/header.html.erb",
17
+ footer: "app/views/avo/partials/footer.html.erb",
18
+ scripts: "app/views/avo/partials/scripts.html.erb"
19
+ }
20
+
21
+ def handle
22
+ if @filename.starts_with?(":")
23
+ template_id = path_to_sym @filename
24
+ template_path = TEMPLATES[template_id]
25
+
26
+ if path_exists? template_path
27
+ eject template_path
28
+ else
29
+ say("Failed to find the `#{template_id.to_sym}` template.", :yellow)
30
+ end
31
+ elsif path_exists? @filename
32
+ eject @filename
33
+ else
34
+ say("Failed to find the `#{@filename}` template.", :yellow)
35
+ end
36
+ end
37
+
38
+ no_tasks do
39
+ def path_to_sym(filename)
40
+ template_id = filename.dup
41
+ template_id[0] = ""
42
+ template_id.to_sym
43
+ end
44
+
45
+ def path_exists?(path)
46
+ path.present? && File.file?(::Avo::Engine.root.join(path))
47
+ end
48
+
49
+ def eject(path)
50
+ copy_file ::Avo::Engine.root.join(path), ::Rails.root.join(path)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,2 @@
1
+ class Avo::ToolsController < Avo::ApplicationController
2
+ end
@@ -0,0 +1 @@
1
+ <%%= render Avo::NavigationLinkComponent.new label: '<%= human_name %>', path: '/avo/<%= file_name %>' %>
@@ -0,0 +1,27 @@
1
+ <div class="flex flex-col">
2
+ <%%= render Avo::PanelComponent.new title: '<%= human_name %>' do |c| %>
3
+ <%% c.tools do %>
4
+ <div class="text-sm italic">This is the panels tools section.</div>
5
+ <%% end %>
6
+
7
+ <%% c.body do %>
8
+ <div class="flex flex-col justify-between py-6 min-h-24">
9
+ <div class="px-6 space-y-4">
10
+ <h3>What a nice new tool 👋</h3>
11
+
12
+ <p>
13
+ You can edit this file here <%= in_code "app/views/avo/tools/#{file_name}.html.erb" %>.
14
+ </p>
15
+
16
+ <p>
17
+ For this tool we created the <%= in_code file_name %> method in the <%= in_code "Avo::ToolsController" %>. You may delete it or move it to your own controller.
18
+ </p>
19
+
20
+ <p>
21
+ A new <%= in_code human_name %> item should have been added to the sidebar. Edit it in <%= in_code "app/views/avo/sidebar/items/_#{file_name}.html.erb" %>
22
+ </p>
23
+ </div>
24
+ </div>
25
+ <%% end %>
26
+ <%% end %>
27
+ </div>