avo 1.0.4 → 1.2.3

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/README.md +3 -1
  5. data/app/components/avo/edit/fields/belongs_to_field_component.html.erb +0 -1
  6. data/app/components/avo/edit/fields/belongs_to_field_component.rb +7 -0
  7. data/app/components/avo/index/grid_cover_empty_state_component.html.erb +3 -0
  8. data/app/components/avo/index/grid_cover_empty_state_component.rb +4 -0
  9. data/app/components/avo/index/grid_item_component.html.erb +4 -4
  10. data/app/components/avo/modal_component.rb +2 -1
  11. data/app/components/avo/panel_component.html.erb +7 -2
  12. data/app/components/avo/panel_component.rb +4 -1
  13. data/app/components/avo/show/fields/has_one_field_component.html.erb +3 -3
  14. data/app/components/avo/views/resource_edit_component.html.erb +3 -3
  15. data/app/components/avo/views/resource_index_component.html.erb +4 -4
  16. data/app/components/avo/views/resource_new_component.html.erb +3 -3
  17. data/app/components/avo/views/resource_show_component.html.erb +2 -2
  18. data/app/controllers/avo/application_controller.rb +12 -8
  19. data/app/controllers/avo/base_controller.rb +16 -0
  20. data/app/controllers/avo/home_controller.rb +2 -0
  21. data/app/helpers/avo/application_helper.rb +0 -30
  22. data/app/packs/entrypoints/application.css +1 -0
  23. data/app/packs/stylesheets/breadcrumbs.css +12 -0
  24. data/app/packs/svgs/chevron-right.svg +3 -0
  25. data/app/views/avo/actions/show.html.erb +2 -2
  26. data/app/views/avo/home/index.html.erb +3 -3
  27. data/{lib/generators/avo/templates → app/views/avo}/partials/_scripts.html.erb +0 -0
  28. data/app/views/avo/relations/new.html.erb +2 -2
  29. data/app/views/avo/sidebar/_sidebar.html.erb +12 -3
  30. data/app/views/layouts/avo/application.html.erb +5 -4
  31. data/avo.gemspec +2 -0
  32. data/lib/avo/app.rb +28 -9
  33. data/lib/avo/base_resource.rb +12 -12
  34. data/lib/avo/configuration.rb +11 -1
  35. data/lib/avo/fields/belongs_to_field.rb +0 -2
  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-13cc02fb.css} +41 -7
  44. data/public/avo-packs/css/application-13cc02fb.css.br +0 -0
  45. data/public/avo-packs/css/application-13cc02fb.css.gz +0 -0
  46. data/public/avo-packs/css/application-13cc02fb.css.map +1 -0
  47. data/public/avo-packs/css/application-13cc02fb.css.map.br +0 -0
  48. data/public/avo-packs/css/application-13cc02fb.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
@@ -14,6 +14,7 @@
14
14
  @import './../stylesheets/alerts.css';
15
15
  @import './../stylesheets/loader.css';
16
16
  @import './../stylesheets/pagination.css';
17
+ @import './../stylesheets/breadcrumbs.css';
17
18
 
18
19
  @import './../stylesheets/components/status.css';
19
20
  @import './../stylesheets/components/code.css';
@@ -0,0 +1,12 @@
1
+ .breadcrumbs {
2
+ @apply relative text-sm text-gray-400 font-bold;
3
+
4
+ a,
5
+ span {
6
+ @apply inline-block text-gray-400;
7
+ }
8
+
9
+ a {
10
+ @apply hover:text-gray-500;
11
+ }
12
+ }
@@ -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,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>
@@ -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
@@ -129,10 +129,28 @@ module Avo
129
129
  .sort_by { |r| r.name }
130
130
  end
131
131
 
132
- def get_navigation_items(user = nil)
132
+ def resources_navigation(user = nil)
133
133
  get_available_resources(user).select do |resource|
134
134
  resource.model_class.present?
135
135
  end
136
+ .select do |resource|
137
+ resource.visible_on_sidebar
138
+ end
139
+ end
140
+
141
+ # Insert any partials that we find in app/views/avo/sidebar/items.
142
+ def get_sidebar_partials
143
+ Dir.glob(Rails.root.join("app", "views", "avo", "sidebar", "items", "*.html.erb"))
144
+ .map do |path|
145
+ File.basename path
146
+ end
147
+ .map do |filename|
148
+ # remove the leading underscore (_)
149
+ filename[0] = ""
150
+ # remove the extension
151
+ filename.gsub!('.html.erb', '')
152
+ filename
153
+ end
136
154
  end
137
155
 
138
156
  def draw_routes
@@ -144,16 +162,17 @@ module Avo
144
162
  .select do |resource|
145
163
  resource != :BaseResource
146
164
  end
165
+ .select do |resource|
166
+ resource.is_a? Class
167
+ end
147
168
  .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
169
+ route_key = if resource.model_class.present?
170
+ resource.model_class.model_name.route_key
171
+ else
172
+ resource.to_s.underscore.gsub("_resource", "").downcase.pluralize.to_sym
156
173
  end
174
+
175
+ resources route_key
157
176
  end
158
177
  end
159
178
  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)
@@ -260,28 +261,27 @@ module Avo
260
261
  end
261
262
  end
262
263
 
263
- # For :new views we're hydrating the model with the values from the resource's default attribute.
264
264
  # We will not overwrite any attributes that come pre-filled in the model.
265
265
  def hydrate_model_with_default_values
266
266
  default_values = get_fields.select do |field|
267
267
  !field.computed
268
268
  end
269
269
  .map do |field|
270
- id = field.id
271
- value = field.value
270
+ id = field.id
271
+ value = field.value
272
272
 
273
- if field.respond_to? :foreign_key
274
- id = field.foreign_key.to_sym
273
+ if field.respond_to? :foreign_key
274
+ id = field.foreign_key.to_sym
275
275
 
276
- reflection = @model._reflections[@params[:via_relation]]
276
+ reflection = @model._reflections[@params[:via_relation]]
277
277
 
278
- if reflection.present? && reflection.foreign_key.present?
279
- value = @params[:via_resource_id]
280
- end
281
- end
278
+ if reflection.present? && reflection.foreign_key.present? && field.id.to_s == @params[:via_relation].to_s
279
+ value = @params[:via_resource_id]
280
+ end
281
+ end
282
282
 
283
- [id, value]
284
- end
283
+ [id, value]
284
+ end
285
285
  .to_h
286
286
  .select do |id, value|
287
287
  value.present?
@@ -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
@@ -50,8 +50,6 @@ module Avo
50
50
  foreign_key.to_sym
51
51
  end
52
52
 
53
- private
54
-
55
53
  def target_resource
56
54
  if @model._reflections[id.to_s].klass.present?
57
55
  App.get_resource_by_model_name @model._reflections[id.to_s].klass.to_s
@@ -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.4"
2
+ VERSION = "1.2.3"
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>
@@ -0,0 +1,62 @@
1
+ require "rails/generators"
2
+ require "fileutils"
3
+
4
+ module Generators
5
+ module Avo
6
+ class ToolGenerator < ::Rails::Generators::Base
7
+ argument :name, type: :string, required: true
8
+
9
+ source_root File.expand_path("templates", __dir__)
10
+
11
+ namespace "avo:tool"
12
+
13
+ def handle
14
+ # Sidebar items
15
+ template "tool/sidebar_item.tt", "app/views/avo/sidebar/items/_#{file_name}.html.erb"
16
+
17
+ # Add controller if it doesn't exist
18
+ controller_path = "app/controllers/avo/tools_controller.rb"
19
+ unless File.file?(Rails.root.join(controller_path))
20
+ template "tool/controller.tt", controller_path
21
+ end
22
+
23
+ # Add controller method
24
+ inject_into_class controller_path, "Avo::ToolsController" do
25
+ <<-METHOD
26
+ def #{file_name}
27
+ @page_title = "#{human_name}"
28
+ add_breadcrumb "#{human_name}"
29
+ end
30
+ METHOD
31
+ end
32
+
33
+ # Add view file
34
+ template "tool/view.tt", "app/views/avo/tools/#{file_name}.html.erb"
35
+
36
+ route <<-ROUTE
37
+ scope :#{::Avo.configuration.root_path.gsub('/', '')} do
38
+ get "#{file_name}", to: "avo/tools##{file_name}"
39
+ end
40
+ ROUTE
41
+ end
42
+
43
+ no_tasks do
44
+ def file_name
45
+ name.to_s.underscore
46
+ end
47
+
48
+ def controller_name
49
+ file_name.to_s
50
+ end
51
+
52
+ def human_name
53
+ file_name.humanize
54
+ end
55
+
56
+ def in_code(text)
57
+ "<code class='p-1 rounded bg-gray-500 text-white text-sm'>#{text}</code>"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end