avo 1.0.0 → 1.1.0.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -5
  3. data/Gemfile.lock +76 -79
  4. data/README.md +3 -1
  5. data/app/components/avo/common/multiple_file_viewer_component.html.erb +1 -1
  6. data/app/components/avo/common/single_file_viewer_component.html.erb +1 -1
  7. data/app/components/avo/edit/fields/belongs_to_field_component.html.erb +0 -1
  8. data/app/components/avo/edit/fields/belongs_to_field_component.rb +7 -0
  9. data/app/components/avo/index/fields/gravatar_field_component.html.erb +1 -1
  10. data/app/components/avo/index/fields/id_field_component.html.erb +1 -1
  11. data/app/components/avo/index/resource_controls_component.html.erb +6 -6
  12. data/app/components/avo/show/fields/has_one_field_component.html.erb +1 -1
  13. data/app/components/avo/views/resource_index_component.html.erb +5 -5
  14. data/app/components/avo/views/resource_show_component.html.erb +3 -3
  15. data/app/controllers/avo/application_controller.rb +7 -8
  16. data/app/helpers/avo/application_helper.rb +0 -24
  17. data/app/views/avo/partials/_empty_state.html.erb +1 -1
  18. data/{lib/generators/avo/templates → app/views/avo}/partials/_scripts.html.erb +0 -0
  19. data/app/views/avo/partials/_view_toggle_button.html.erb +2 -2
  20. data/app/views/avo/relations/new.html.erb +1 -1
  21. data/app/views/avo/sidebar/_sidebar.html.erb +11 -2
  22. data/app/views/layouts/avo/application.html.erb +3 -3
  23. data/config/webpacker.yml +12 -1
  24. data/lib/avo/app.rb +14 -1
  25. data/lib/avo/base_resource.rb +11 -12
  26. data/lib/avo/fields/belongs_to_field.rb +0 -2
  27. data/lib/avo/version.rb +1 -1
  28. data/lib/generators/avo/eject_generator.rb +55 -0
  29. data/lib/generators/avo/templates/tool/controller.tt +2 -0
  30. data/lib/generators/avo/templates/tool/sidebar_item.tt +1 -0
  31. data/lib/generators/avo/templates/tool/view.tt +27 -0
  32. data/lib/generators/avo/tool_generator.rb +60 -0
  33. data/public/avo-packs/css/application-9d115b7e.css.map +1 -1
  34. data/public/avo-packs/css/application-9d115b7e.css.map.br +0 -0
  35. data/public/avo-packs/css/application-9d115b7e.css.map.gz +0 -0
  36. metadata +10 -10
  37. data/config/webpacker_packed.yml +0 -79
  38. data/lib/generators/avo/partials_generator.rb +0 -14
  39. data/lib/generators/avo/templates/partials/_footer.html.erb +0 -3
  40. data/lib/generators/avo/templates/partials/_header.html.erb +0 -1
  41. data/lib/generators/avo/templates/partials/_logo.html.erb +0 -1
@@ -4,19 +4,19 @@
4
4
 
5
5
  <% if can_create? %>
6
6
  <%= a_link create_path do %>
7
- <%= svg 'plus' %> <%= t('avo.create_new_item', { item: @resource.model_class.model_name.human.downcase }) %>
7
+ <%= svg 'plus' %> <%= t('avo.create_new_item', item: @resource.model_class.model_name.human.downcase ) %>
8
8
  <% end %>
9
9
  <% end %>
10
10
 
11
11
  <% if can_detach? %>
12
12
  <%= a_link detach_path, color: 'indigo', method: :delete, data: { confirm: "Are you sure you want to detach this #{@resource.singular_name}." } do %>
13
- <%= svg 'trash' %> <%= t('avo.detach_item', { item: @resource.singular_name }).capitalize %>
13
+ <%= svg 'trash' %> <%= t('avo.detach_item', item: @resource.singular_name).capitalize %>
14
14
  <% end %>
15
15
  <% end %>
16
16
 
17
17
  <% if can_attach? %>
18
18
  <%= a_link attach_path, color: 'indigo', 'data-turbo-frame': 'attach_modal' do %>
19
- <%= svg 'view-grid-add' %> <%= t('avo.attach_item', { item: @resource.singular_name }).capitalize %>
19
+ <%= svg 'view-grid-add' %> <%= t('avo.attach_item', item: @resource.singular_name).capitalize %>
20
20
  <% end %>
21
21
  <% end %>
22
22
  <% end %>
@@ -43,7 +43,7 @@
43
43
 
44
44
  <% if @models.present? %>
45
45
  <div class="py-4">
46
- <%= render partial: 'avo/partials/paginator', locals: { pagy: @pagy, turbo_frame: @turbo_frame } %>
46
+ <%= render partial: 'avo/partials/paginator', locals: { pagy: @pagy, turbo_frame: @turbo_frame || 'none' } %>
47
47
  </div>
48
48
  <% end %>
49
49
  </div>
@@ -55,7 +55,7 @@
55
55
  <%= render Avo::Index::ResourceGridComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model) %>
56
56
 
57
57
  <div class="bg-white rounded-xl shadow-xl mt-8 py-6">
58
- <%= render partial: 'avo/partials/paginator', locals: { pagy: @pagy, turbo_frame: @turbo_frame } %>
58
+ <%= render partial: 'avo/partials/paginator', locals: { pagy: @pagy, turbo_frame: @turbo_frame || 'none' } %>
59
59
  </div>
60
60
  <% end %>
61
61
  <% end %>
@@ -10,7 +10,7 @@
10
10
 
11
11
  <% if @reflection.present? && @resource.model.present? %>
12
12
  <%= a_link detach_path, color: 'indigo', method: :delete, data: { confirm: "Are you sure you want to detach this #{@resource.singular_name}." } do %>
13
- <%= svg 'trash' %> <%= t('avo.detach_item', { item: @resource.singular_name }).capitalize %>
13
+ <%= svg 'trash' %> <%= t('avo.detach_item', item: @resource.singular_name).capitalize %>
14
14
  <% end %>
15
15
  <% else %>
16
16
  <%= a_link back_path do %>
@@ -19,12 +19,12 @@
19
19
 
20
20
  <% if @resource.authorization.authorize_action(:destroy, raise_exception: false) %>
21
21
  <%= form_with url: helpers.resource_path(@resource.model), method: :delete, html: { 'data-turbo-frame': params[:turbo_frame] } do |form| %>
22
- <%= a_button title: t('avo.delete_item', { item: @resource.model.model_name.name.downcase }).capitalize,
22
+ <%= a_button title: t('avo.delete_item', item: @resource.model.model_name.name.downcase).capitalize,
23
23
  color: 'red',
24
24
  variant: 'outlined',
25
25
  type: :submit,
26
26
  data: {
27
- confirm: t('avo.are_you_sure', { item: @resource.model.model_name.name.downcase }),
27
+ confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
28
28
  control: :destroy,
29
29
  'resource-id': @resource.model.id,
30
30
  'tippy': 'tooltip',
@@ -51,7 +51,7 @@ module Avo
51
51
  existing_params = Addressable::URI.parse(request.fullpath).query_values.symbolize_keys
52
52
  end
53
53
  rescue; end
54
- send :"resources_#{model.model_name.route_key}_path", **existing_params, **args
54
+ avo.send :"resources_#{model.model_name.route_key}_path", **existing_params, **args
55
55
  end
56
56
 
57
57
  def related_resources_path(parent_model, model, keep_query_params: false, **args)
@@ -70,9 +70,9 @@ module Avo
70
70
  end
71
71
 
72
72
  def resource_path(model = nil, resource_id: nil, keep_query_params: false, **args)
73
- return send :"resources_#{model.model_name.route_key.singularize}_path", resource_id, **args if resource_id.present?
73
+ return avo.send :"resources_#{model.model_name.route_key.singularize}_path", resource_id, **args if resource_id.present?
74
74
 
75
- send :"resources_#{model.model_name.route_key.singularize}_path", model, **args
75
+ avo.send :"resources_#{model.model_name.route_key.singularize}_path", model, **args
76
76
  end
77
77
 
78
78
  def resource_attach_path(model_name, model_id, related_name, related_id = nil)
@@ -92,11 +92,11 @@ module Avo
92
92
  end
93
93
 
94
94
  def new_resource_path(model, **args)
95
- send :"new_resources_#{model.model_name.route_key.singularize}_path", **args
95
+ avo.send :"new_resources_#{model.model_name.route_key.singularize}_path", **args
96
96
  end
97
97
 
98
98
  def edit_resource_path(model, **args)
99
- send :"edit_resources_#{model.model_name.route_key.singularize}_path", model, **args
99
+ avo.send :"edit_resources_#{model.model_name.route_key.singularize}_path", model, **args
100
100
  end
101
101
 
102
102
  private
@@ -110,6 +110,8 @@ module Avo
110
110
  end
111
111
 
112
112
  def set_resource
113
+ raise ActionController::RoutingError.new "No route matches" if resource.nil?
114
+
113
115
  @resource = resource.hydrate(params: params)
114
116
  end
115
117
 
@@ -182,8 +184,6 @@ module Avo
182
184
  def eager_load_files(resource, query)
183
185
  if resource.attached_file_fields.present?
184
186
  resource.attached_file_fields.map do |field|
185
- # abort "#{field.pluralize}".inspect
186
- # abort field.class.inspect
187
187
  attachment = case field.class.to_s
188
188
  when "Avo::Fields::FileField"
189
189
  "attachment"
@@ -194,7 +194,6 @@ module Avo
194
194
  end
195
195
 
196
196
  return query.eager_load "#{field.id}_#{attachment}": :blob
197
- # return query.send :"with_attached_#{field}"
198
197
  end
199
198
  end
200
199
 
@@ -7,30 +7,6 @@ module Avo
7
7
  Avo.webpacker
8
8
  end
9
9
 
10
- def render_logo
11
- render partial: "vendor/avo/partials/logo"
12
- rescue
13
- render partial: "avo/partials/logo"
14
- end
15
-
16
- def render_header
17
- render partial: "vendor/avo/partials/header"
18
- rescue
19
- render partial: "avo/partials/header"
20
- end
21
-
22
- def render_footer
23
- render partial: "vendor/avo/partials/footer"
24
- rescue
25
- render partial: "avo/partials/footer"
26
- end
27
-
28
- def render_scripts
29
- render partial: "vendor/avo/partials/scripts"
30
- rescue
31
- ""
32
- end
33
-
34
10
  def render_license_warnings
35
11
  render partial: "avo/sidebar/license_warnings", locals: {
36
12
  license: Avo::App.license.properties
@@ -1,7 +1,7 @@
1
1
  <%
2
2
  classes = 'absolute inset-auto left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2'
3
3
  translation_tag = params[:related_name].present? ? 'avo.no_related_item_found' : 'avo.no_item_found'
4
- label = t translation_tag, { item: resource_name }
4
+ label = t translation_tag, item: resource_name
5
5
  %>
6
6
  <div class="relative flex-1 py-12">
7
7
  <div class="relative block text-gray-300 h-64 w-full">
@@ -11,7 +11,7 @@
11
11
  translation_key = 'avo.table_view'
12
12
  end
13
13
  %>
14
- <%= a_link params.permit!.merge(view_type: new_view_type), color: :blue, 'data-turbo-frame': turbo_frame, title: t('avo.switch_to_view', { view_type: new_view_type }), 'data-tippy': 'tooltip' do %>
15
- <%= svg new_icon %> <%= t(translation_key, { item: @resource.model_class.model_name.human.downcase }) %>
14
+ <%= a_link params.permit!.merge(view_type: new_view_type), color: :blue, 'data-turbo-frame': turbo_frame, title: t('avo.switch_to_view', view_type: new_view_type), 'data-tippy': 'tooltip' do %>
15
+ <%= svg new_icon %> <%= t translation_key, item: @resource.model_class.model_name.human.downcase %>
16
16
  <% end %>
17
17
  <% end %>
@@ -6,7 +6,7 @@
6
6
  } do |form| %>
7
7
  <%= render Avo::ModalComponent.new do |c| %>
8
8
  <% c.with :heading do %>
9
- <%= t('avo.choose_item', { item: params[:related_name].downcase }) %>
9
+ <%= t 'avo.choose_item', item: params[:related_name].downcase %>
10
10
  <% end %>
11
11
 
12
12
  <div class="flex-1 flex items-center justify-center px-8 text-lg mt-8 mb-12">
@@ -1,7 +1,7 @@
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">
@@ -10,9 +10,18 @@
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>
@@ -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,6 @@
44
44
  <% end %>
45
45
  </div>
46
46
 
47
- <%= render_scripts %>
47
+ <%= render partial: "avo/partials/footer" %>
48
48
  </body>
49
49
  </html>
data/config/webpacker.yml CHANGED
@@ -5,21 +5,32 @@ default: &default
5
5
  source_entry_path: entrypoints
6
6
  public_root_path: public
7
7
  cache_path: tmp/cache/webpacker
8
+ check_yarn_integrity: false
8
9
  # use a different sub-folder name
9
10
  public_output_path: avo-packs
10
11
  webpack_compile_output: true
12
+ extract_css: true
11
13
 
12
14
  # Additional paths webpack should lookup modules
13
15
  # ['app/assets', 'engine/foo/app/assets']
14
- additional_paths: []
16
+ additional_paths_paths: []
15
17
 
16
18
  # Reload manifest.json on all requests so we reload latest compiled packs
17
19
  cache_manifest: false
18
20
 
21
+ # We override the development env. when packed.
22
+ # Avo needs to be packed and compiled for when the dev uses it in his development env.
19
23
  development:
20
24
  <<: *default
25
+
26
+ # Production depends on precompilation of packs prior to booting for performance.
21
27
  compile: true
22
28
 
29
+ # Cache manifest.json for performance
30
+ cache_manifest: true
31
+
32
+ # Even though Avo does not use the dev_server when packed we still need to add the info here.
33
+ # The packed version of Avo will pick up the parent's app information.
23
34
  # Reference: https://webpack.js.org/configuration/dev-server/
24
35
  dev_server:
25
36
  https: false
data/lib/avo/app.rb CHANGED
@@ -129,12 +129,25 @@ 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
136
  end
137
137
 
138
+ # Insert any partials that we find in app/views/avo/sidebar/items.
139
+ def get_sidebar_partials
140
+ Dir.glob(Rails.root.join("app", "views", "avo", "sidebar", "items", "*.html.erb"))
141
+ .map do |path|
142
+ File.basename path
143
+ end
144
+ .map do |filename|
145
+ # remove the leading underscore (_)
146
+ filename[0] = ""
147
+ filename
148
+ end
149
+ end
150
+
138
151
  def draw_routes
139
152
  # We should eager load all the classes so we find all descendants
140
153
  Rails.application.eager_load!
@@ -260,28 +260,27 @@ module Avo
260
260
  end
261
261
  end
262
262
 
263
- # For :new views we're hydrating the model with the values from the resource's default attribute.
264
263
  # We will not overwrite any attributes that come pre-filled in the model.
265
264
  def hydrate_model_with_default_values
266
265
  default_values = get_fields.select do |field|
267
266
  !field.computed
268
267
  end
269
268
  .map do |field|
270
- id = field.id
271
- value = field.value
269
+ id = field.id
270
+ value = field.value
272
271
 
273
- if field.respond_to? :foreign_key
274
- id = field.foreign_key.to_sym
272
+ if field.respond_to? :foreign_key
273
+ id = field.foreign_key.to_sym
275
274
 
276
- reflection = @model._reflections[@params[:via_relation]]
275
+ reflection = @model._reflections[@params[:via_relation]]
277
276
 
278
- if reflection.present? && reflection.foreign_key.present?
279
- value = @params[:via_resource_id]
280
- end
281
- end
277
+ if reflection.present? && reflection.foreign_key.present? && field.id.to_s == @params[:via_relation].to_s
278
+ value = @params[:via_resource_id]
279
+ end
280
+ end
282
281
 
283
- [id, value]
284
- end
282
+ [id, value]
283
+ end
285
284
  .to_h
286
285
  .select do |id, value|
287
286
  value.present?
@@ -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
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0.pre.1"
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
+ <%%= panel title: '<%= human_name %>' do |c| %>
3
+ <%% c.with :tools do %>
4
+ <div class="text-sm italic">This is the panels tools section.</div>
5
+ <%% end %>
6
+
7
+ <%% c.with :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>