avo 3.0.0.pre8 → 3.0.0.pre11

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/stylesheets/avo.base.css +0 -1
  4. data/app/assets/svgs/map-empty-state.svg +35 -0
  5. data/app/assets/svgs/map-view-type.svg +3 -0
  6. data/app/components/avo/actions_component.rb +4 -2
  7. data/app/components/avo/field_wrapper_component.html.erb +2 -2
  8. data/app/components/avo/fields/area_field/edit_component.html.erb +7 -0
  9. data/app/components/avo/fields/area_field/edit_component.rb +4 -0
  10. data/app/components/avo/fields/area_field/show_component.html.erb +8 -0
  11. data/app/components/avo/fields/area_field/show_component.rb +4 -0
  12. data/app/components/avo/fields/common/files/controls_component.html.erb +29 -0
  13. data/app/components/avo/fields/common/files/controls_component.rb +19 -0
  14. data/app/components/avo/fields/common/files/list_viewer_component.html.erb +20 -0
  15. data/app/components/avo/fields/common/files/list_viewer_component.rb +41 -0
  16. data/app/components/avo/fields/common/files/view_type/grid_component.html.erb +27 -0
  17. data/app/components/avo/fields/common/{single_file_viewer_component.rb → files/view_type/grid_component.rb} +2 -9
  18. data/app/components/avo/fields/common/files/view_type/list_component.html.erb +22 -0
  19. data/app/components/avo/fields/common/files/view_type/list_component.rb +15 -0
  20. data/app/components/avo/fields/country_field/edit_component.html.erb +2 -1
  21. data/app/components/avo/fields/file_field/edit_component.html.erb +1 -1
  22. data/app/components/avo/fields/file_field/show_component.html.erb +1 -1
  23. data/app/components/avo/fields/files_field/edit_component.html.erb +1 -1
  24. data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
  25. data/app/components/avo/fields/location_field/show_component.html.erb +1 -1
  26. data/app/components/avo/fields/number_field/edit_component.html.erb +2 -1
  27. data/app/components/avo/fields/password_field/edit_component.html.erb +2 -1
  28. data/app/components/avo/fields/select_field/edit_component.html.erb +2 -1
  29. data/app/components/avo/fields/text_field/edit_component.html.erb +2 -1
  30. data/app/components/avo/index/resource_map_component.html.erb +16 -0
  31. data/app/components/avo/index/resource_map_component.rb +109 -0
  32. data/app/components/avo/resource_component.rb +37 -1
  33. data/app/components/avo/views/resource_index_component.html.erb +13 -4
  34. data/app/controllers/avo/actions_controller.rb +1 -4
  35. data/app/controllers/avo/application_controller.rb +22 -4
  36. data/app/controllers/avo/attachments_controller.rb +15 -8
  37. data/app/controllers/avo/base_controller.rb +0 -5
  38. data/app/views/avo/actions/show.html.erb +2 -1
  39. data/app/views/avo/attachments/destroy.turbo_stream.erb +7 -0
  40. data/app/views/avo/debug/status.html.erb +2 -1
  41. data/app/views/avo/partials/_view_toggle_button.html.erb +9 -0
  42. data/config/routes.rb +2 -2
  43. data/db/factories.rb +15 -0
  44. data/lib/avo/app.rb +13 -6
  45. data/lib/avo/base_action.rb +5 -5
  46. data/lib/avo/base_resource.rb +9 -9
  47. data/lib/avo/concerns/has_items.rb +1 -1
  48. data/lib/avo/engine.rb +0 -1
  49. data/lib/avo/fields/area_field.rb +39 -0
  50. data/lib/avo/fields/base_field.rb +6 -2
  51. data/lib/avo/fields/files_field.rb +4 -0
  52. data/lib/avo/fields/location_field.rb +0 -1
  53. data/lib/avo/plugin.rb +10 -0
  54. data/lib/avo/resources/resource_manager.rb +7 -11
  55. data/lib/avo/services/debug_service.rb +1 -0
  56. data/lib/avo/services/telemetry_service.rb +1 -0
  57. data/lib/avo/version.rb +1 -1
  58. data/lib/avo.rb +1 -1
  59. data/lib/generators/avo/install_generator.rb +11 -0
  60. data/lib/generators/avo/tailwindcss/install_generator.rb +4 -1
  61. data/lib/generators/avo/templates/tailwindcss/Procfile.dev +1 -1
  62. data/lib/tasks/avo_tasks.rake +5 -0
  63. data/public/avo-assets/avo.base.css +1395 -1349
  64. metadata +20 -6
  65. data/app/components/avo/fields/common/files_list_viewer_component.html.erb +0 -5
  66. data/app/components/avo/fields/common/files_list_viewer_component.rb +0 -8
  67. data/app/components/avo/fields/common/single_file_viewer_component.html.erb +0 -57
@@ -14,8 +14,8 @@
14
14
  <% c.with_body do %>
15
15
  <div class="flex flex-col">
16
16
  <%= render scopes_list if can_render_scopes? %>
17
- <div class="flex flex-col py-4 gap-4">
18
- <div class="flex flex-col xs:flex-row xs:justify-between space-y-2 xs:space-y-0 <%= 'hidden' unless header_visible? %>">
17
+ <div class="flex flex-col">
18
+ <div class="flex flex-col xs:flex-row xs:justify-between space-y-2 xs:space-y-0 py-4 <%= 'hidden' unless header_visible? %>">
19
19
  <div class="flex items-center px-4 w-64">
20
20
  <% if show_search_input %>
21
21
  <%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.route_key, via_reflection: via_reflection} %>
@@ -34,7 +34,7 @@
34
34
  </div>
35
35
  </div>
36
36
  <% if Avo.avo_filters_installed? && resource.has_filters? %>
37
- <%= render AvoFilters::FiltersComponent.new resource: resource %>
37
+ <%= render AvoFilters::FiltersComponent.new resource: resource, turbo_frame: turbo_frame %>
38
38
  <% end %>
39
39
  </div>
40
40
  </div>
@@ -49,7 +49,16 @@
49
49
  <% end %>
50
50
  <% end %>
51
51
  <% c.with_bare_content do %>
52
- <% if view_type.to_sym == :table %>
52
+ <% if view_type.to_sym == :map %>
53
+ <% if @resources.present? %>
54
+ <div>
55
+ <%= render(Avo::Index::ResourceMapComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_record: @parent_record, parent_resource: @parent_resource, pagy: @pagy, query: @query)) %>
56
+ </div>
57
+ <% else %>
58
+ <%= helpers.empty_state by_association: params[:related_name].present?, view_type: view_type, add_background: true %>
59
+ <% end %>
60
+ <% end %>
61
+ <% if view_type.to_sym == :table || view_type.to_sym == :map %>
53
62
  <% if @records.present? %>
54
63
  <div class="mt-4">
55
64
  <%= render Avo::PaginatorComponent.new pagy: @pagy, turbo_frame: turbo_frame || 'none', index_params: @index_params, resource: @resource, parent_record: parent_record, discreet_pagination: field&.discreet_pagination %>
@@ -61,11 +61,8 @@ module Avo
61
61
  end
62
62
 
63
63
  def action_class
64
- klass_name = params[:action_id].gsub("avo_actions_", "").camelize
65
- klass_name = "Avo::Actions::#{klass_name}"
66
-
67
64
  Avo::BaseAction.descendants.find do |action|
68
- action.to_s == klass_name
65
+ action.to_s == params[:action_id]
69
66
  end
70
67
  end
71
68
 
@@ -107,14 +107,32 @@ module Avo
107
107
  def set_record_to_fill
108
108
  @record_to_fill = @resource.model_class.new if @view == :create
109
109
  @record_to_fill = @record if @view == :update
110
+
111
+ # If resource.record is nil, most likely the user is creating a new record.
112
+ # In that case, to access resource.record in visible and readonly blocks we hydrate the resource with a new record.
113
+ @resource.hydrate(record: @record_to_fill) if @resource.record.nil?
110
114
  end
111
115
 
112
116
  def fill_record
113
117
  # We have to skip filling the the record if this is an attach action
114
- is_attach_action = params[model_param_key].blank? && params[:related_name].present? && params[:fields].present?
118
+ return if is_attach_action?
119
+
120
+ @record = @resource.fill_record(@record_to_fill, cast_nullable(model_params), extra_params: extra_params)
121
+ assign_default_value_to_disabled_fields if @view == :create
122
+ end
123
+
124
+ def is_attach_action?
125
+ params[model_param_key].blank? && params[:related_name].present? && params[:fields].present?
126
+ end
115
127
 
116
- unless is_attach_action
117
- @record = @resource.fill_record(@record_to_fill, cast_nullable(model_params), extra_params: extra_params)
128
+ def assign_default_value_to_disabled_fields
129
+ @resource.get_field_definitions.select do |field|
130
+ field.is_disabled? && field.visible? && !field.computed
131
+ end.each do |field|
132
+ # Get the default value from the field default definition
133
+ # If there is no default value specified on the resource, get the value from the record (DB, Callbacks, etc.)
134
+ default_value = field.default || @record.send(field.id)
135
+ field.fill_field @record, field.id, default_value, params
118
136
  end
119
137
  end
120
138
 
@@ -285,7 +303,7 @@ module Avo
285
303
  if Rails::VERSION::MAJOR === 6
286
304
  ActiveStorage::Current.host = request.base_url
287
305
  elsif Rails::VERSION::MAJOR === 7
288
- ActiveStorage::Current.url_options = request.base_url
306
+ ActiveStorage::Current.url_options = {protocol: request.protocol, host: request.host, port: request.port}
289
307
  end
290
308
  end
291
309
  rescue => exception
@@ -23,17 +23,24 @@ module Avo
23
23
  end
24
24
 
25
25
  def destroy
26
- raise Avo::NotAuthorizedError.new unless authorized_to :delete
26
+ if authorized_to :delete
27
+ attachment = ActiveStorage::Attachment.find(params[:attachment_id])
27
28
 
28
- attachment = ActiveStorage::Attachment.find(params[:attachment_id])
29
- path = resource_path(record: @record, resource: @resource)
29
+ flash[:notice] = if attachment.present?
30
+ @destroyed = attachment.destroy
30
31
 
31
- if attachment.present?
32
- attachment.destroy
33
-
34
- redirect_to params[:referrer] || path, notice: t("avo.attachment_destroyed")
32
+ t("avo.attachment_destroyed")
33
+ else
34
+ t("avo.failed_to_find_attachment")
35
+ end
35
36
  else
36
- redirect_back fallback_location: path, notice: t("avo.failed_to_find_attachment")
37
+ flash[:notice] = t("avo.not_authorized")
38
+ end
39
+
40
+ respond_to do |format|
41
+ format.turbo_stream do
42
+ render "destroy"
43
+ end
37
44
  end
38
45
  end
39
46
 
@@ -29,11 +29,6 @@ module Avo
29
29
  @query = @resource.class.query_scope
30
30
  end
31
31
 
32
- # Remove default_scope for index view if no parent_resource present
33
- if @resource.unscoped_queries_on_index && @parent_resource.blank?
34
- @query = @query.unscoped
35
- end
36
-
37
32
  # Eager load the associations
38
33
  if @resource.includes.present?
39
34
  @query = @query.includes(*@resource.includes)
@@ -9,7 +9,7 @@
9
9
  >
10
10
  <%= form_with model: @record,
11
11
  scope: 'fields',
12
- url: "#{@resource.records_path}/actions/#{@action.param_id}",
12
+ url: "#{@resource.records_path}/actions",
13
13
  local: true,
14
14
  data: @action.class.form_data_attributes do |form|
15
15
  %>
@@ -20,6 +20,7 @@
20
20
  <div class="flex-1 flex">
21
21
  <%= @action.get_message %>
22
22
  </div>
23
+ <%= hidden_field_tag :action_id, @action.param_id %>
23
24
  <%= form.hidden_field :avo_resource_ids, value: params[:id] || params[:resource_ids], 'data-action-target': 'resourceIds' %>
24
25
  <%= form.hidden_field :avo_selected_query, 'data-action-target': 'selectedAllQuery' %>
25
26
  <% if @action.get_fields.present? %>
@@ -0,0 +1,7 @@
1
+ <%= turbo_stream.remove dom_id(@destroyed) if @destroyed.present? %>
2
+
3
+ <turbo-stream action="append" target="alerts">
4
+ <template>
5
+ <%= render Avo::FlashAlertsComponent.new flashes: flash %>
6
+ </template>
7
+ </turbo-stream>
@@ -74,8 +74,9 @@
74
74
  <div class="flex flex-col flex-1">
75
75
  <div>
76
76
  <ul>
77
+ <li>Avo <%= Avo::VERSION %></li>
77
78
  <% Avo.plugin_manager.plugins.each do |plugin| %>
78
- <li><%= plugin.klass.to_s.split("::").first %></li>
79
+ <li><%= plugin.klass.to_s.split("::").first %> <%= plugin.klass.version %></li>
79
80
  <% end %>
80
81
  </ul>
81
82
  </div>
@@ -11,6 +11,15 @@
11
11
  new_icon: 'grid-view-type',
12
12
  translation_key: 'avo.grid_view',
13
13
  },
14
+ list: {
15
+ new_view_type: 'list',
16
+ new_icon: 'queue-list',
17
+ },
18
+ map: {
19
+ new_view_type: 'map',
20
+ new_icon: 'map-view-type',
21
+ translation_key: 'avo.map_view',
22
+ },
14
23
  }
15
24
  %>
16
25
  <div class="flex">
data/config/routes.rb CHANGED
@@ -23,8 +23,8 @@ Avo::Engine.routes.draw do
23
23
  delete "/:resource_name/:id/active_storage_attachments/:attachment_name/:attachment_id", to: "attachments#destroy"
24
24
 
25
25
  # Actions
26
- get "/:resource_name(/:id)/actions/:action_id", to: "actions#show"
27
- post "/:resource_name(/:id)/actions/:action_id", to: "actions#handle"
26
+ get "/:resource_name(/:id)/actions/(:action_id)", to: "actions#show"
27
+ post "/:resource_name(/:id)/actions/(:action_id)", to: "actions#handle"
28
28
 
29
29
  # Generate resource routes as below:
30
30
  # resources :posts
data/db/factories.rb CHANGED
@@ -48,6 +48,20 @@ FactoryBot.define do
48
48
  progress { Faker::Number.between(from: 0, to: 100) }
49
49
  end
50
50
 
51
+ trait :with_files do
52
+ after(:create) do |project|
53
+ ["watch.jpg", "dummy-video.mp4"].each do |filename|
54
+ file = Rails.root.join("db", "seed_files", filename)
55
+ project.files.attach(io: file.open, filename: filename)
56
+ end
57
+
58
+ ["dummy-file.txt", "dummy-audio.mp3"].each do |filename|
59
+ file = Avo::Engine.root.join("spec", "dummy", filename)
60
+ project.files.attach(io: file.open, filename: filename)
61
+ end
62
+ end
63
+ end
64
+
51
65
  factory :comment do
52
66
  body { Faker::Lorem.paragraphs(number: rand(4...10)).join(" ") }
53
67
  posted_at { Time.now - rand(10...365).days }
@@ -104,6 +118,7 @@ FactoryBot.define do
104
118
  description { Faker::Address.community }
105
119
  status { ["open", "closed"].sample }
106
120
  tiny_description { Faker::Address.community }
121
+ city_center_area { [[[10.0, 11.2], [10.5, 11.9], [10.8, 12.0], [10.0, 11.2]]].to_json }
107
122
  end
108
123
 
109
124
  factory :product do
data/lib/avo/app.rb CHANGED
@@ -28,15 +28,22 @@ module Avo
28
28
  def boot
29
29
  init_logger
30
30
  init_fields
31
- init_cache_store
31
+ self.cache_store = get_cache_store
32
+ ::Avo.plugin_manager.boot_plugins
32
33
  end
33
34
 
34
- def init_cache_store
35
- # The cache store should be set when the app boots up.
36
- if Rails.cache.instance_of?(ActiveSupport::Cache::NullStore)
37
- self.cache_store ||= ActiveSupport::Cache::MemoryStore.new
35
+ def get_cache_store
36
+ if Rails.env.production?
37
+ case Rails.cache.class.to_s
38
+ when "ActiveSupport::Cache::MemCacheStore", "ActiveSupport::Cache::RedisCacheStore"
39
+ Rails.cache
40
+ else
41
+ ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
42
+ end
43
+ elsif Rails.env.test?
44
+ Rails.cache
38
45
  else
39
- self.cache_store = Rails.cache
46
+ ActiveSupport::Cache.lookup_store(:memory_store)
40
47
  end
41
48
  end
42
49
 
@@ -59,10 +59,10 @@ module Avo
59
59
  end
60
60
 
61
61
  def initialize(record: nil, resource: nil, user: nil, view: nil, arguments: {})
62
- self.class.record = record if record.present?
63
- self.class.resource = resource if resource.present?
64
- self.class.user = user if user.present?
65
- self.class.view = view if view.present?
62
+ self.class.record = record
63
+ self.class.resource = resource
64
+ self.class.user = user
65
+ self.class.view = view
66
66
  @arguments = arguments
67
67
 
68
68
  self.class.message ||= I18n.t("avo.are_you_sure_you_want_to_run_this_option")
@@ -149,7 +149,7 @@ module Avo
149
149
  end
150
150
 
151
151
  def param_id
152
- self.class.to_s.demodulize.underscore.tr "/", "_"
152
+ self.class.to_s
153
153
  end
154
154
 
155
155
  def succeed(text)
@@ -52,8 +52,9 @@ module Avo
52
52
  class_attribute :filters_loader
53
53
  class_attribute :grid_loader
54
54
  class_attribute :visible_on_sidebar, default: true
55
- class_attribute :unscoped_queries_on_index, default: false
56
- class_attribute :index_query
55
+ class_attribute :index_query, default: -> {
56
+ query
57
+ }
57
58
  class_attribute :find_record_method, default: -> {
58
59
  query.find id
59
60
  }
@@ -64,6 +65,7 @@ module Avo
64
65
  class_attribute :keep_filters_panel_open, default: false
65
66
  class_attribute :extra_params
66
67
  class_attribute :link_to_child_resource, default: false
68
+ class_attribute :map_view
67
69
 
68
70
  # EXTRACT:
69
71
  class_attribute :ordering
@@ -109,13 +111,10 @@ module Avo
109
111
 
110
112
  # This resolves the scope when doing "where" queries (not find queries)
111
113
  def query_scope
112
- final_scope = if index_query.present?
113
- Avo::ExecutionContext.new(target: index_query, query: model_class).handle
114
- else
115
- model_class
116
- end
117
-
118
- authorization.apply_policy final_scope
114
+ authorization.apply_policy Avo::ExecutionContext.new(
115
+ target: index_query,
116
+ query: model_class
117
+ ).handle
119
118
  end
120
119
 
121
120
  # This resolves the scope when finding records (not "where" queries)
@@ -335,6 +334,7 @@ module Avo
335
334
  view_types = [:table]
336
335
 
337
336
  view_types << :grid if get_grid_fields.present?
337
+ view_types << :map if map_view.present?
338
338
 
339
339
  view_types
340
340
  end
@@ -6,7 +6,7 @@ module Avo
6
6
  class_methods do
7
7
  def deprecated_dsl_api(name = nil)
8
8
  message = "This API was deprecated. Please use the `#{name}` method inside the `fields` method."
9
- raise DeprecatedAPI.new message
9
+ raise DeprecatedAPIError.new message
10
10
  end
11
11
 
12
12
  # DSL methods
data/lib/avo/engine.rb CHANGED
@@ -24,7 +24,6 @@ module Avo
24
24
 
25
25
  # Boot Avo
26
26
  ::Avo::App.boot
27
- ::Avo.plugin_manager.boot_plugins
28
27
 
29
28
  # After deploy we want to make sure the license response is being cleared.
30
29
  # We need a fresh license response.
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Avo
4
+ module Fields
5
+ class AreaField < BaseField
6
+ attr_reader :mapkick_options
7
+ attr_reader :datapoint_options
8
+
9
+ def initialize(id, **args, &block)
10
+ hide_on :index
11
+
12
+ super(id, **args, &block)
13
+
14
+ @geometry = args[:geometry].presence || :polygon # Accepts: `:polygon` or `:multi_polygon`
15
+ @mapkick_options = args[:mapkick_options].presence || {}
16
+ @datapoint_options = args[:datapoint_options].presence || {}
17
+ end
18
+
19
+ def map_data
20
+ data_source = {
21
+ geometry: {
22
+ type: @geometry.to_s.classify,
23
+ coordinates: value
24
+ }
25
+ }
26
+
27
+ [data_source.merge(datapoint_options)]
28
+ end
29
+
30
+ def coordinates
31
+ value.present? ? JSON.parse(value) : []
32
+ end
33
+
34
+ def geometry
35
+ @geometry.to_s.classify
36
+ end
37
+ end
38
+ end
39
+ end
@@ -33,6 +33,7 @@ module Avo
33
33
  attr_reader :nullable
34
34
  attr_reader :null_values
35
35
  attr_reader :format_using
36
+ attr_reader :autocomplete
36
37
  attr_reader :help
37
38
  attr_reader :default
38
39
  attr_reader :as_label
@@ -42,7 +43,6 @@ module Avo
42
43
  attr_reader :for_presentation_only
43
44
 
44
45
  # Private options
45
- attr_reader :updatable
46
46
  attr_reader :computable # if allowed to be computable
47
47
  attr_reader :computed # if block is present
48
48
  attr_reader :computed_value # the value after computation
@@ -68,6 +68,7 @@ module Avo
68
68
  @null_values = args[:null_values] || [nil, ""]
69
69
  @format_using = args[:format_using] || nil
70
70
  @placeholder = args[:placeholder]
71
+ @autocomplete = args[:autocomplete] || nil
71
72
  @help = args[:help] || nil
72
73
  @default = args[:default] || nil
73
74
  @visible = args[:visible]
@@ -83,7 +84,6 @@ module Avo
83
84
 
84
85
  @args = args
85
86
 
86
- @updatable = !disabled
87
87
  @computable = true
88
88
  @computed = block.present?
89
89
  @computed_value = nil
@@ -264,6 +264,10 @@ module Avo
264
264
  options
265
265
  end
266
266
 
267
+ def updatable
268
+ !is_disabled? && visible?
269
+ end
270
+
267
271
  private
268
272
 
269
273
  def model_or_class(model)
@@ -6,6 +6,8 @@ module Avo
6
6
  attr_accessor :direct_upload
7
7
  attr_accessor :accept
8
8
  attr_reader :display_filename
9
+ attr_reader :view_type
10
+ attr_reader :hide_view_type_switcher
9
11
 
10
12
  def initialize(id, **args, &block)
11
13
  super(id, **args, &block)
@@ -15,6 +17,8 @@ module Avo
15
17
  @direct_upload = args[:direct_upload].present? ? args[:direct_upload] : false
16
18
  @accept = args[:accept].present? ? args[:accept] : nil
17
19
  @display_filename = args[:display_filename].nil? ? true : args[:display_filename]
20
+ @view_type = args[:view_type] || :grid
21
+ @hide_view_type_switcher = args[:hide_view_type_switcher]
18
22
  end
19
23
 
20
24
  def view_component_name
@@ -7,7 +7,6 @@ module Avo
7
7
 
8
8
  def initialize(id, **args, &block)
9
9
  hide_on :index
10
-
11
10
  super(id, **args, &block)
12
11
 
13
12
  @stored_as = args[:stored_as].present? ? args[:stored_as] : nil # You can pass it an array of db columns [:latitude, :longitude]
data/lib/avo/plugin.rb CHANGED
@@ -2,5 +2,15 @@ module Avo
2
2
  class Plugin
3
3
  def initialize(*, **, &block)
4
4
  end
5
+
6
+ class << self
7
+ def version
8
+ "#{namespace}::VERSION".safe_constantize
9
+ end
10
+
11
+ def namespace
12
+ to_s.split("::").first
13
+ end
14
+ end
5
15
  end
6
16
  end
@@ -90,16 +90,12 @@ module Avo
90
90
 
91
91
  # Filters out the resources that are missing the model_class
92
92
  def valid_resources
93
- resources
94
- .select do |resource|
95
- resource.model_class.present?
96
- end
97
- .sort_by(&:to_s)
93
+ resources.select { |resource| resource.model_class.present? }.sort_by(&:name)
98
94
  end
99
95
 
100
96
  # Returns the Avo resource by camelized name
101
97
  #
102
- # get_resource_by_name('User') => Avo::Resources::User
98
+ # get_resource_by_name('User') => instance of Avo::Resources::User
103
99
  def get_resource(resource)
104
100
  resource = "Avo::Resources::#{resource}" unless resource.to_s.starts_with?("Avo::Resources::")
105
101
 
@@ -110,7 +106,7 @@ module Avo
110
106
 
111
107
  # Returns the Avo resource by singular snake_cased name
112
108
  #
113
- # get_resource_by_name('user') => Avo::Resources::User
109
+ # get_resource_by_name('user') => instance of Avo::Resources::User
114
110
  def get_resource_by_name(name)
115
111
  get_resource name.singularize.camelize
116
112
  end
@@ -118,8 +114,8 @@ module Avo
118
114
  # Returns the Avo resource by singular snake_cased name
119
115
  # From all the resources that use the same model_class, it will fetch the first one in alphabetical order
120
116
  #
121
- # get_resource_by_name('User') => Avo::Resources::User
122
- # get_resource_by_name(User) => Avo::Resources::User
117
+ # get_resource_by_name('User') => instance of Avo::Resources::User
118
+ # get_resource_by_name(User) => instance of Avo::Resources::User
123
119
 
124
120
  def get_resource_by_model_class(klass)
125
121
  # Fetch the mappings imposed by the user.
@@ -135,8 +131,8 @@ module Avo
135
131
 
136
132
  # Returns the Avo resource by singular snake_cased name
137
133
  #
138
- # get_resource_by_controller_name('delayed_backend_active_record_jobs') => DelayedJobResource
139
- # get_resource_by_controller_name('users') => Avo::Resources::User
134
+ # get_resource_by_controller_name('delayed_backend_active_record_jobs') => instance of Avo::Resources::DelayedJob
135
+ # get_resource_by_controller_name('users') => instance of Avo::Resources::User
140
136
  def get_resource_by_controller_name(name)
141
137
  valid_resources
142
138
  .find do |resource|
@@ -59,6 +59,7 @@ class Avo::Services::DebugService
59
59
  **other_metadata(:filters),
60
60
  main_menu_present: Avo.configuration.main_menu.present?,
61
61
  profile_menu_present: Avo.configuration.profile_menu.present?,
62
+ cache_store: Avo::App.cache_store&.class&.to_s,
62
63
  **config_metadata
63
64
  }
64
65
  # rescue => error
@@ -51,6 +51,7 @@ class Avo::Services::TelemetryService
51
51
  **other_metadata(:filters),
52
52
  main_menu_present: Avo.configuration.main_menu.present?,
53
53
  profile_menu_present: Avo.configuration.profile_menu.present?,
54
+ cache_store: Avo::App.cache_store&.class&.to_s,
54
55
  **config_metadata
55
56
  }
56
57
  # rescue => error
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "3.0.0.pre8" unless const_defined?(:VERSION)
2
+ VERSION = "3.0.0.pre11" unless const_defined?(:VERSION)
3
3
  end
data/lib/avo.rb CHANGED
@@ -61,7 +61,7 @@ module Avo
61
61
 
62
62
  class MissingGemError < StandardError; end
63
63
 
64
- class DeprecatedAPI < StandardError; end
64
+ class DeprecatedAPIError < StandardError; end
65
65
 
66
66
  class << self
67
67
  def avo_filters_installed?
@@ -14,6 +14,17 @@ module Generators
14
14
 
15
15
  template "initializer/avo.tt", "config/initializers/avo.rb"
16
16
  directory File.join(__dir__, "templates", "locales"), "config/locales"
17
+ create_resources
18
+ end
19
+
20
+ def create_resources
21
+ if defined?(User).present?
22
+ Rails::Generators.invoke("avo:resource", ["user", "-q"], {destination_root: Rails.root })
23
+ end
24
+
25
+ if defined?(Account).present?
26
+ Rails::Generators.invoke("avo:resource", ["account", "-q"], {destination_root: Rails.root })
27
+ end
17
28
  end
18
29
  end
19
30
  end
@@ -21,7 +21,7 @@ module Generators
21
21
  end
22
22
 
23
23
  if Rails.root.join("Procfile.dev").exist?
24
- append_to_file "Procfile.dev", "avo_css: bin/rails avo:tailwindcss:watch\n"
24
+ append_to_file "Procfile.dev", "avo_css: yarn avo:tailwindcss --watch\n"
25
25
  else
26
26
  say "Add default Procfile.dev"
27
27
  copy_file template_path("Procfile.dev"), "Procfile.dev"
@@ -38,6 +38,9 @@ module Generators
38
38
 
39
39
  say "Adding the CSS asset to the partial"
40
40
  prepend_to_file Rails.root.join("app", "views", "avo", "partials", "_pre_head.html.erb"), "<%= stylesheet_link_tag \"avo.tailwind.css\", media: \"all\" %>"
41
+
42
+ say "Ensure you have the following script in your package.json file.", :yellow
43
+ say %("scripts": { "avo:tailwindcss": "tailwindcss -i ./app/assets/stylesheets/avo.tailwind.css -o ./app/assets/builds/avo.tailwind.css --minify" }), :green
41
44
  end
42
45
 
43
46
  no_tasks do
@@ -1,2 +1,2 @@
1
1
  web: bin/rails server -p 3000
2
- avo_css: bin/rails avo:tailwindcss:watch
2
+ avo_css: yarn avo:tailwindcss --watch
@@ -3,6 +3,11 @@
3
3
  # # Task goes here
4
4
  # end
5
5
 
6
+ desc "Runs the update command for all Avo gems."
7
+ task "avo:update" do
8
+ system "bundle update avo avo_pro avo_advanced avo_dashboards avo_filters avo_menu avo_upgrade"
9
+ end
10
+
6
11
  desc "Installs Avo assets and bundles them for when you want to use the GitHub repo in your app"
7
12
  task "avo:build-assets" do
8
13
  spec = get_gem_spec "avo"