avo 1.4.1 → 1.4.5.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +7 -1
  4. data/README.md +4 -4
  5. data/app/components/avo/fields/common/status_viewer_component.html.erb +13 -5
  6. data/app/components/avo/views/resource_index_component.html.erb +3 -3
  7. data/app/controllers/avo/application_controller.rb +14 -7
  8. data/app/controllers/avo/relations_controller.rb +1 -1
  9. data/app/controllers/avo/search_controller.rb +50 -35
  10. data/app/packs/entrypoints/application.css +2 -1
  11. data/app/packs/entrypoints/application.js +3 -0
  12. data/app/packs/js/controllers/search_controller.js +120 -0
  13. data/app/packs/js/helpers/debounce_promise.js +13 -0
  14. data/app/packs/stylesheets/components/status.css +31 -20
  15. data/app/packs/stylesheets/search.css +79 -0
  16. data/app/packs/svgs/check-circle-sm.svg +3 -0
  17. data/app/packs/svgs/x-circle-sm.svg +3 -0
  18. data/app/views/avo/partials/_global_search.html.erb +17 -0
  19. data/app/views/avo/partials/_resource_search.html.erb +10 -0
  20. data/app/views/layouts/avo/application.html.erb +2 -4
  21. data/avo.gemspec +1 -0
  22. data/config/routes.rb +2 -6
  23. data/lib/avo/app.rb +3 -1
  24. data/lib/avo/base_resource.rb +83 -7
  25. data/lib/avo/configuration.rb +2 -0
  26. data/lib/avo/fields/base_field.rb +6 -0
  27. data/lib/avo/fields/external_image_field.rb +1 -0
  28. data/lib/avo/fields/file_field.rb +4 -0
  29. data/lib/avo/licensing/license.rb +10 -0
  30. data/lib/avo/licensing/pro_license.rb +3 -1
  31. data/lib/avo/services/authorization_service.rb +1 -1
  32. data/lib/avo/version.rb +1 -1
  33. data/lib/generators/avo/templates/initializer/avo.tt +1 -0
  34. data/lib/generators/avo/templates/resource/resource.tt +3 -0
  35. data/public/avo-packs/css/{application-cb627e0b.css → application-d69fe930.css} +211 -20
  36. data/public/avo-packs/css/application-d69fe930.css.br +0 -0
  37. data/public/avo-packs/css/application-d69fe930.css.gz +0 -0
  38. data/public/avo-packs/css/application-d69fe930.css.map +1 -0
  39. data/public/avo-packs/css/application-d69fe930.css.map.br +0 -0
  40. data/public/avo-packs/css/application-d69fe930.css.map.gz +0 -0
  41. data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js +26 -0
  42. data/public/avo-packs/js/{application-6a0b9e58526ae6bef242.js.LICENSE.txt → application-7b3d507875f4bc1f6677.js.LICENSE.txt} +0 -0
  43. data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.br +0 -0
  44. data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.gz +0 -0
  45. data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.map +1 -0
  46. data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.map.br +0 -0
  47. data/public/avo-packs/js/application-7b3d507875f4bc1f6677.js.map.gz +0 -0
  48. data/public/avo-packs/manifest.json +15 -15
  49. metadata +38 -17
  50. data/public/avo-packs/css/application-cb627e0b.css.br +0 -0
  51. data/public/avo-packs/css/application-cb627e0b.css.gz +0 -0
  52. data/public/avo-packs/css/application-cb627e0b.css.map +0 -1
  53. data/public/avo-packs/css/application-cb627e0b.css.map.br +0 -0
  54. data/public/avo-packs/css/application-cb627e0b.css.map.gz +0 -0
  55. data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js +0 -26
  56. data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.br +0 -0
  57. data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.gz +0 -0
  58. data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.map +0 -1
  59. data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.map.br +0 -0
  60. data/public/avo-packs/js/application-6a0b9e58526ae6bef242.js.map.gz +0 -0
@@ -1,30 +1,41 @@
1
1
  .spinner {
2
+ width: 16px;
3
+ height: 16px;
4
+
2
5
  position: relative;
3
- display: inline-block;
4
- width: 18px;
5
- height: 18px;
6
- top: 3px;
6
+ }
7
+
8
+
9
+ .double-bounce1, .double-bounce2 {
10
+ width: 100%;
11
+ height: 100%;
12
+ border-radius: 50%;
7
13
  background-color: #333;
8
- border-radius: 100%;
9
- -webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
10
- animation: sk-scaleout 1.0s infinite ease-in-out;
14
+ opacity: 0.6;
15
+ position: absolute;
16
+ top: 0;
17
+ left: 0;
18
+
19
+ -webkit-animation: sk-bounce 2.0s infinite ease-in-out;
20
+ animation: sk-bounce 2.0s infinite ease-in-out;
11
21
  }
12
22
 
13
- @-webkit-keyframes sk-scaleout {
14
- 0% { -webkit-transform: scale(0) }
15
- 100% {
16
- -webkit-transform: scale(1.0);
17
- opacity: 0;
18
- }
23
+ .double-bounce2 {
24
+ -webkit-animation-delay: -1.0s;
25
+ animation-delay: -1.0s;
19
26
  }
20
27
 
21
- @keyframes sk-scaleout {
22
- 0% {
23
- -webkit-transform: scale(0);
24
- transform: scale(0);
25
- } 100% {
26
- -webkit-transform: scale(1.0);
28
+ @-webkit-keyframes sk-bounce {
29
+ 0%, 100% { -webkit-transform: scale(0.0) }
30
+ 50% { -webkit-transform: scale(1.0) }
31
+ }
32
+
33
+ @keyframes sk-bounce {
34
+ 0%, 100% {
35
+ transform: scale(0.0);
36
+ -webkit-transform: scale(0.0);
37
+ } 50% {
27
38
  transform: scale(1.0);
28
- opacity: 0;
39
+ -webkit-transform: scale(1.0);
29
40
  }
30
41
  }
@@ -0,0 +1,79 @@
1
+ :root {
2
+ --aa-primary-color: --tw-ring-color;
3
+ --aa-selected-color: --tw-ring-color;
4
+ --aa-primary-color-rgb: 5, 150, 105;
5
+ }
6
+
7
+ .global-search {
8
+ .aa-DetachedSearchButton:focus,
9
+ .aa-DetachedSearchButton {
10
+ border: none !important;
11
+ }
12
+ }
13
+
14
+ .resource-search {
15
+ .aa-Autocomplete {
16
+ @apply w-full;
17
+ }
18
+
19
+ .aa-DetachedSearchButton {
20
+ @apply rounded-full border-gray-300;
21
+ }
22
+ }
23
+
24
+ .aa-SourceHeader {
25
+ @apply uppercase text-xs font-semibold;
26
+ }
27
+
28
+ .aa-DetachedFormContainer,
29
+ .aa-DetachedContainer .aa-Panel {
30
+ @apply bg-blue-gray-100 border-b-0;
31
+ }
32
+
33
+ .aa-Form {
34
+ &:focus-within {
35
+ @apply ring-0 !important;
36
+ }
37
+
38
+ input {
39
+ @apply focus:ring-0 !important;
40
+ }
41
+ &:focus-within {
42
+ .aa-InputWrapperPrefix{
43
+ .aa-SubmitButton {
44
+ @apply ring-0;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ .aa-Input {
50
+ @apply focus:ring-green-600;
51
+ }
52
+
53
+ .aa-DetachedContainer {
54
+ .aa-PanelLayout {
55
+ @apply pt-0 space-y-3;
56
+ }
57
+
58
+ .aa-Source {
59
+ .aa-List {
60
+ @apply space-y-1;
61
+
62
+ .aa-Item {
63
+ @apply bg-white rounded-md px-3 py-4 shadow;
64
+
65
+ .aa-ItemDescription {
66
+ @apply text-gray-500 text-sm;
67
+ }
68
+
69
+ &[aria-selected=true]{
70
+ @apply bg-blue-700 text-white;
71
+
72
+ .aa-ItemDescription {
73
+ @apply text-white;
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,3 @@
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="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
3
+ </svg>
@@ -0,0 +1,3 @@
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="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
3
+ </svg>
@@ -0,0 +1,17 @@
1
+ <div data-controller="search" class="global-search" data-turbo-remove-before-cache>
2
+ <div class="inline-block"
3
+ data-search-target="autocomplete"
4
+ data-search-resource="global"
5
+ data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
6
+ data-debounce-timeout='<%= Avo.configuration.search_debounce %>'
7
+ >
8
+ </div>
9
+ <div class="relative top-[-5px] inline-flex text-gray-400 text-sm leading-5 py-0.5 px-1.5 border border-gray-300 rounded-md cursor-pointer"
10
+ data-search-target="button"
11
+ >
12
+ <span class="sr-only">Press </span>
13
+ <kbd class="font-sans"><abbr title="Command" class="no-underline">⌘</abbr></kbd>
14
+ <span class="sr-only"> and </span>
15
+ <kbd class="font-sans">K</kbd><span class="sr-only"> to search</span>
16
+ </div>
17
+ </div>
@@ -0,0 +1,10 @@
1
+ <div data-controller="search" class="resource-search flex items-center h-full w-full" data-turbo-remove-before-cache>
2
+ <div class="w-full"
3
+ data-search-target="autocomplete"
4
+ data-search-resource="<%= resource %>"
5
+ data-translation-keys='{"no_item_found": "<%= I18n.translate 'avo.no_item_found' %>"}'
6
+ data-debounce-timeout='<%= Avo.configuration.search_debounce %>'
7
+ >
8
+ </div>
9
+ <div class="relative inline-flex text-gray-400 text-sm border border-gray-300 rounded-full cursor-pointer" data-search-target="button"></div>
10
+ </div>
@@ -23,10 +23,8 @@
23
23
  <div>
24
24
  <%= render partial: "avo/partials/header" %>
25
25
  </div>
26
- <div class="flex-1 flex justify-center">
27
- <div class="w-64 flex">
28
- <%# <resources-search :global="true"> %>
29
- </div>
26
+ <div class="flex-1 flex ml-4 pl-4">
27
+ <%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) %>
30
28
  </div>
31
29
  <div class="align-end">
32
30
  <%= render partial: "avo/partials/profile_dropdown" %>
data/avo.gemspec CHANGED
@@ -45,4 +45,5 @@ Gem::Specification.new do |spec|
45
45
  spec.add_dependency "meta-tags"
46
46
  spec.add_dependency "breadcrumbs_on_rails"
47
47
  spec.add_dependency "manifester"
48
+ spec.add_dependency "ransack"
48
49
  end
data/config/routes.rb CHANGED
@@ -4,6 +4,8 @@ Avo::Engine.routes.draw do
4
4
  get "resources", to: redirect("/avo")
5
5
 
6
6
  scope "avo_api", as: "avo_api" do
7
+ get "/search", to: "search#index"
8
+ get "/:resource_name/search", to: "search#show"
7
9
  post "/resources/:resource_name/:id/attachments/", to: "attachments#create"
8
10
  end
9
11
 
@@ -27,10 +29,4 @@ Avo::Engine.routes.draw do
27
29
  post "/:resource_name/:id/:related_name", to: "relations#create"
28
30
  delete "/:resource_name/:id/:related_name/:related_id", to: "relations#destroy"
29
31
  end
30
-
31
- # get '/avo-api/search', to: 'search#index'
32
- # get '/avo-api/:resource_name/search', to: 'search#resource'
33
-
34
- # Tools
35
- # get '/avo-tools/resource-overview', to: 'resource_overview#index'
36
32
  end
data/lib/avo/app.rb CHANGED
@@ -6,6 +6,7 @@ module Avo
6
6
  class_attribute :request, default: nil
7
7
  class_attribute :context, default: nil
8
8
  class_attribute :license, default: nil
9
+ class_attribute :current_user, default: nil
9
10
 
10
11
  class << self
11
12
  def boot
@@ -20,9 +21,10 @@ module Avo
20
21
  end
21
22
  end
22
23
 
23
- def init(request:, context:)
24
+ def init(request:, context:, current_user:)
24
25
  self.request = request
25
26
  self.context = context
27
+ self.current_user = current_user
26
28
 
27
29
  self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
28
30
 
@@ -11,7 +11,7 @@ module Avo
11
11
 
12
12
  class_attribute :id, default: :id
13
13
  class_attribute :title, default: :id
14
- class_attribute :search, default: [:id]
14
+ class_attribute :search_query, default: nil
15
15
  class_attribute :includes, default: []
16
16
  class_attribute :model_class
17
17
  class_attribute :translation_key
@@ -42,6 +42,18 @@ module Avo
42
42
 
43
43
  self.filters_loader.use filter_class
44
44
  end
45
+
46
+ def scope
47
+ authorization.apply_policy model_class
48
+ end
49
+
50
+ def authorization
51
+ Avo::Services::AuthorizationService.new Avo::App.current_user
52
+ end
53
+ end
54
+
55
+ def initialize
56
+ self.class.model_class = model_class
45
57
  end
46
58
 
47
59
  def hydrate(model: nil, view: nil, user: nil, params: nil)
@@ -65,7 +77,7 @@ module Avo
65
77
  field.hydrate(resource: self, panel_name: default_panel_name, user: user)
66
78
  end
67
79
 
68
- if Avo::App.license.invalid? || Avo::App.license.lacks(:custom_fields)
80
+ if Avo::App.license.has_with_trial(:custom_fields)
69
81
  fields = fields.reject do |field|
70
82
  field.custom?
71
83
  end
@@ -96,9 +108,9 @@ module Avo
96
108
  end
97
109
  end
98
110
 
99
- fields.map do |field|
100
- field.hydrate(model: @model, view: @view, resource: self)
101
- end
111
+ hydrate_fields(model: @model, view: @view)
112
+
113
+ fields
102
114
  end
103
115
 
104
116
  def get_grid_fields
@@ -119,8 +131,16 @@ module Avo
119
131
  self.class.actions_loader.bag
120
132
  end
121
133
 
134
+ def hydrate_fields(model: nil, view: nil)
135
+ fields.map do |field|
136
+ field.hydrate(model: @model, view: @view, resource: self)
137
+ end
138
+
139
+ self
140
+ end
141
+
122
142
  def default_panel_name
123
- return @params[:related_name].capitalize if @params[:related_name].present?
143
+ return @params[:related_name].capitalize if @params.present? && @params[:related_name].present?
124
144
 
125
145
  case @view
126
146
  when :show
@@ -226,7 +246,11 @@ module Avo
226
246
 
227
247
  def fill_model(model, params)
228
248
  # Map the received params to their actual fields
229
- fields_by_database_id = get_field_definitions.map { |field| [field.database_id(model).to_s, field] }.to_h
249
+ fields_by_database_id = get_field_definitions
250
+ .reject do |field|
251
+ field.computed
252
+ end
253
+ .map { |field| [field.database_id(model).to_s, field] }.to_h
230
254
 
231
255
  params.each do |key, value|
232
256
  field = fields_by_database_id[key]
@@ -301,5 +325,57 @@ module Avo
301
325
  end
302
326
  end
303
327
  end
328
+
329
+ def avo_path
330
+ "#{Avo.configuration.root_path}/resources/#{model_class.model_name.route_key}/#{model.id}"
331
+ end
332
+
333
+ def label_field
334
+ get_field_definitions.find do |field|
335
+ field.as_label.present?
336
+ end
337
+ rescue
338
+ nil
339
+ end
340
+
341
+ def label
342
+ label_field.value || model_title
343
+ rescue
344
+ model_title
345
+ end
346
+
347
+ def avatar_field
348
+ get_field_definitions.find do |field|
349
+ field.as_avatar.present?
350
+ end
351
+ rescue
352
+ nil
353
+ end
354
+
355
+ def avatar
356
+ avatar_field.to_image
357
+ rescue
358
+ nil
359
+ end
360
+
361
+ def avatar_type
362
+ avatar_field.as_avatar
363
+ rescue
364
+ nil
365
+ end
366
+
367
+ def description_field
368
+ get_field_definitions.find do |field|
369
+ field.as_description.present?
370
+ end
371
+ rescue
372
+ nil
373
+ end
374
+
375
+ def description
376
+ description_field.value
377
+ rescue
378
+ nil
379
+ end
304
380
  end
305
381
  end
@@ -22,6 +22,7 @@ module Avo
22
22
  attr_accessor :display_breadcrumbs
23
23
  attr_accessor :initial_breadcrumbs
24
24
  attr_accessor :home_path
25
+ attr_accessor :search_debounce
25
26
 
26
27
  def initialize
27
28
  @root_path = "/avo"
@@ -56,6 +57,7 @@ module Avo
56
57
  }
57
58
  @display_breadcrumbs = true
58
59
  @home_path = nil
60
+ @search_debounce = 300
59
61
  end
60
62
 
61
63
  def locale_tag
@@ -19,6 +19,9 @@ module Avo
19
19
  attr_reader :help
20
20
  attr_reader :default
21
21
  attr_reader :visible
22
+ attr_reader :as_label
23
+ attr_reader :as_avatar
24
+ attr_reader :as_description
22
25
 
23
26
  # Private options
24
27
  attr_reader :updatable
@@ -53,6 +56,9 @@ module Avo
53
56
  @help = args[:help] || nil
54
57
  @default = args[:default] || nil
55
58
  @visible = args[:visible] || true
59
+ @as_label = args[:as_label] || false
60
+ @as_avatar = args[:as_avatar] || false
61
+ @as_description = args[:as_description] || false
56
62
 
57
63
  @updatable = true
58
64
  @computable = true
@@ -4,6 +4,7 @@ module Avo
4
4
  attr_reader :width
5
5
  attr_reader :height
6
6
  attr_reader :radius
7
+ attr_reader :link_to_resource
7
8
 
8
9
  def initialize(id, **args, &block)
9
10
  super(id, **args, &block)
@@ -20,6 +20,10 @@ module Avo
20
20
  def variant(resize_to_limit: [128, 128])
21
21
  value.variant(resize_to_limit: resize_to_limit).processed.service_url
22
22
  end
23
+
24
+ def to_image
25
+ return variant(resize_to_limit: [320, 320]) if is_image
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -47,6 +47,16 @@ module Avo
47
47
 
48
48
  alias_method :has, :can
49
49
  alias_method :lacks, :cant
50
+
51
+ # Returns if has ability and if is a valid license or app is in development.
52
+ def has_with_trial(ability)
53
+ can(ability) && (valid? || Rails.env.development? || Rails.env.test?)
54
+ end
55
+
56
+ # Returns if lacks ability and if is a valid license or app is in development.
57
+ def lacks_with_trial(ability)
58
+ !can(ability) && (valid? || Rails.env.development? || Rails.env.test?)
59
+ end
50
60
  end
51
61
  end
52
62
  end