avo 2.5.2.pre.5 → 2.6.0

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 +2 -0
  3. data/Gemfile.lock +4 -3
  4. data/app/assets/stylesheets/avo.css +2 -0
  5. data/app/assets/stylesheets/css/tags.css +16 -0
  6. data/app/components/avo/actions_component.html.erb +1 -1
  7. data/app/components/avo/button_component.rb +49 -18
  8. data/app/components/avo/card_component.rb +12 -0
  9. data/app/components/avo/fields/concerns/item_labels.rb +40 -0
  10. data/app/components/avo/fields/tags_field/edit_component.html.erb +27 -0
  11. data/app/components/avo/fields/tags_field/edit_component.rb +4 -0
  12. data/app/components/avo/fields/tags_field/index_component.html.erb +10 -0
  13. data/app/components/avo/fields/tags_field/index_component.rb +9 -0
  14. data/app/components/avo/fields/tags_field/show_component.html.erb +7 -0
  15. data/app/components/avo/fields/tags_field/show_component.rb +7 -0
  16. data/app/components/avo/fields/tags_field/tag_component.html.erb +9 -0
  17. data/app/components/avo/fields/tags_field/tag_component.rb +11 -0
  18. data/app/components/avo/index/resource_table_component.html.erb +1 -1
  19. data/app/components/avo/sidebar/group_component.html.erb +3 -3
  20. data/app/components/avo/sidebar/heading_component.html.erb +2 -2
  21. data/app/components/avo/sidebar/link_component.html.erb +1 -1
  22. data/app/components/avo/sidebar/link_component.rb +1 -1
  23. data/app/components/avo/sidebar_profile_component.html.erb +1 -1
  24. data/app/components/avo/views/resource_index_component.html.erb +2 -2
  25. data/app/components/avo/views/resource_show_component.html.erb +1 -1
  26. data/app/controllers/avo/base_controller.rb +0 -19
  27. data/app/controllers/avo/private_controller.rb +1 -0
  28. data/app/javascript/js/application.js +1 -1
  29. data/app/javascript/js/controllers/base_controller.js +22 -0
  30. data/app/javascript/js/controllers/fields/tags_field_controller.js +86 -0
  31. data/app/javascript/js/controllers/fields/tags_field_helpers.js +47 -0
  32. data/app/javascript/js/controllers/menu_controller.js +2 -2
  33. data/app/javascript/js/controllers.js +2 -0
  34. data/app/views/avo/dashboards/_chartkick_card.html.erb +1 -1
  35. data/app/views/avo/dashboards/_metric_card.html.erb +1 -1
  36. data/app/views/avo/partials/_navbar.html.erb +3 -3
  37. data/app/views/avo/private/_links_and_buttons.html.erb +1 -1
  38. data/app/views/layouts/avo/application.html.erb +1 -1
  39. data/db/factories.rb +2 -0
  40. data/lib/avo/base_resource.rb +6 -0
  41. data/lib/avo/concerns/handles_field_args.rb +36 -0
  42. data/lib/avo/fields/base_field.rb +2 -1
  43. data/lib/avo/fields/tags_field.rb +82 -0
  44. data/lib/avo/hosts/record_host.rb +7 -0
  45. data/lib/avo/licensing/pro_license.rb +2 -1
  46. data/lib/avo/version.rb +1 -1
  47. data/lib/generators/avo/templates/cards/chartkick_card.tt +1 -1
  48. data/lib/generators/avo/templates/cards/chartkick_card_sample.tt +1 -1
  49. data/lib/generators/avo/templates/cards/metric_card.tt +1 -1
  50. data/lib/generators/avo/templates/cards/metric_card_sample.tt +1 -1
  51. data/lib/generators/avo/templates/locales/avo.en.yml +4 -0
  52. data/lib/tasks/avo_tasks.rake +7 -3
  53. data/public/avo-assets/avo.css +722 -71
  54. data/public/avo-assets/avo.js +211 -122
  55. data/public/avo-assets/avo.js.map +3 -3
  56. metadata +20 -7
  57. data/app/assets/builds/avo.css +0 -9032
  58. data/app/assets/builds/avo.js +0 -423
  59. data/app/assets/builds/avo.js.map +0 -7
@@ -0,0 +1,47 @@
1
+ export function tagTemplate(tagData) {
2
+ const suggestions = this.settings.whitelist || []
3
+
4
+ const possibleSuggestion = suggestions.find(
5
+ // eslint-disable-next-line eqeqeq
6
+ (item) => item.value == tagData.value,
7
+ )
8
+ const possibleLabel = possibleSuggestion
9
+ ? possibleSuggestion.label
10
+ : tagData.value
11
+
12
+ return `
13
+ <tag title="${tagData.value}"
14
+ contenteditable='false'
15
+ spellcheck='false'
16
+ tabIndex="-1"
17
+ class="tagify__tag ${tagData.class ? tagData.class : ''}"
18
+ ${this.getAttributes(tagData)}
19
+ >
20
+ <x title='' class='tagify__tag__removeBtn' role='button' aria-label='remove tag'></x>
21
+ <div>
22
+ <span class='tagify__tag-text'>${possibleLabel}</span>
23
+ </div>
24
+ </tag>
25
+ `
26
+ }
27
+
28
+ export function suggestionItemTemplate(tagData) {
29
+ return `
30
+ <div ${this.getAttributes(tagData)}
31
+ class='tagify__dropdown__item flex items-center ${
32
+ tagData.class ? tagData.class : ''
33
+ }'
34
+ tabindex="0"
35
+ role="option">
36
+ ${
37
+ tagData.avatar
38
+ ? `
39
+ <div class='rounded w-8 h-8 block mr-2'>
40
+ <img onerror="this.style.visibility='hidden'" class="w-full" src="${tagData.avatar}">
41
+ </div>`
42
+ : ''
43
+ }
44
+ <span>${tagData.label}</span>
45
+ </div>
46
+ `
47
+ }
@@ -54,12 +54,12 @@ export default class extends Controller {
54
54
  }
55
55
 
56
56
  markCollapsed() {
57
- this.svgTarget.classList.add('rotate-180')
57
+ this.svgTarget.classList.add('rotate-90')
58
58
  this.itemsTarget.classList.add('hidden')
59
59
  }
60
60
 
61
61
  markExpanded() {
62
- this.svgTarget.classList.remove('rotate-180')
62
+ this.svgTarget.classList.remove('rotate-90')
63
63
  this.itemsTarget.classList.remove('hidden')
64
64
  }
65
65
  }
@@ -24,6 +24,7 @@ import SearchController from './controllers/search_controller'
24
24
  import SelectController from './controllers/select_controller'
25
25
  import SelectFilterController from './controllers/select_filter_controller'
26
26
  import SimpleMdeController from './controllers/fields/simple_mde_controller'
27
+ import TagsFieldController from './controllers/fields/tags_field_controller'
27
28
  import TextFilterController from './controllers/text_filter_controller'
28
29
  import TippyController from './controllers/tippy_controller'
29
30
  import TogglePanelController from './controllers/toggle_panel_controller'
@@ -48,6 +49,7 @@ application.register('per-page', PerPageController)
48
49
  application.register('search', SearchController)
49
50
  application.register('select', SelectController)
50
51
  application.register('select-filter', SelectFilterController)
52
+ application.register('tags-field', TagsFieldController)
51
53
  application.register('text-filter', TextFilterController)
52
54
  application.register('tippy', TippyController)
53
55
  application.register('toggle-panel', TogglePanelController)
@@ -1,3 +1,3 @@
1
1
  <div class="relative flex flex-col <%= @card.chartkick_classes %>">
2
- <%= send(@card.chart_type, @card.compute_result.result_data, **@card.chartkick_options) %>
2
+ <%= send(@card.chart_type, @card.result_data, **@card.chartkick_options) %>
3
3
  </div>
@@ -1,5 +1,5 @@
1
1
  <div class="flex mt-4 items-end">
2
2
  <span class="text-3xl"><%= @card.prefix %></span>
3
- <span class="text-5xl"><%= @card.compute_result.result_data %></span>
3
+ <span class="text-5xl"><%= @card.result_data %></span>
4
4
  <span class="text-3xl"><%= @card.suffix %></span>
5
5
  </div>
@@ -1,12 +1,12 @@
1
1
  <div
2
- class="fixed bg-white p-2 w-full flex flex-shrink-0 items-center z-50 px-4 lg:px-4 border-b space-x-4 lg:space-x-0 min-h-[4rem] <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
2
+ class="fixed bg-white p-2 w-full flex flex-shrink-0 items-center z-50 px-4 lg:px-4 border-b space-x-4 lg:space-x-0 h-16 <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>"
3
3
  v-if="layout !== 'blank'"
4
4
  >
5
- <div class="flex items-center space-x-2 lg:space-x-0 w-64">
5
+ <div class="flex items-center space-x-2 lg:space-x-0 w-32 sm:w-64 flex-shrink-0">
6
6
  <%= a_button class: 'lg:hidden', icon: 'menu', size: :xs, compact: true, style: :text, data: { action: 'click->mobile#toggleSidebar' } %>
7
7
  <%= render partial: "avo/partials/logo" %>
8
8
  </div>
9
- <div class="flex-1 flex items-center justify-between lg:justify-start space-x-8 lg:pl-4">
9
+ <div class="flex-1 flex items-center justify-between lg:justify-start space-x-2 sm:space-x-8 lg:pl-4">
10
10
  <div class="flex">
11
11
  <%= render partial: "avo/partials/global_search" if ::Avo::App.license.has_with_trial(:global_search) && ::Avo.configuration.feature_enabled?(:global_search) %>
12
12
  </div>
@@ -1,6 +1,6 @@
1
1
  <%
2
2
  entities = [:button, :link]
3
- sizes = [:lg, :md, :sm, :xs]
3
+ sizes = [:xl, :lg, :md, :sm, :xs].reverse
4
4
  styles = [:primary, :outline, :text]
5
5
  colors = [:primary, :gray, :red, :orange, :green]
6
6
  states = [:regular, :hover, :disabled, :active]
@@ -25,7 +25,7 @@
25
25
  <div class="flex-1 flex pt-16 relative">
26
26
  <%= render Avo::SidebarComponent.new %>
27
27
  <div class="lg:pl-64 flex-1 flex flex-col min-h-full max-w-full">
28
- <div class="content py-4 lg:p-4 px-4 flex-1 flex flex-col justify-between items-stretch <%= @container_classes %>">
28
+ <div class="content p-4 lg:p-6 flex-1 flex flex-col justify-between items-stretch <%= @container_classes %>">
29
29
  <%= render partial: "avo/partials/custom_tools_alert" if @custom_tools_alert_visible %>
30
30
  <div class="flex flex-1 flex-col justify-between items-stretch space-y-8">
31
31
  <%= yield %>
data/db/factories.rb CHANGED
@@ -25,6 +25,7 @@ FactoryBot.define do
25
25
  Time.now - rand(10...365).days
26
26
  end
27
27
  end
28
+ tag_list { ["1", "2", "five", "seven"].shuffle }
28
29
  status { ::Post.statuses.keys.sample }
29
30
  end
30
31
 
@@ -64,6 +65,7 @@ FactoryBot.define do
64
65
 
65
66
  factory :course do
66
67
  name { Faker::Educator.unique.course_name }
68
+ skills { [Faker::Educator.subject, Faker::Educator.subject, Faker::Educator.subject, Faker::Educator.subject, Faker::Educator.subject] }
67
69
  country { Course.countries.sample }
68
70
  city { Course.cities.stringify_keys[country].sample }
69
71
  end
@@ -136,6 +136,12 @@ module Avo
136
136
  end
137
137
  end
138
138
 
139
+ if Avo::App.license.lacks_with_trial(:advanced_fields)
140
+ fields = fields.reject do |field|
141
+ field.type == 'tags'
142
+ end
143
+ end
144
+
139
145
  fields
140
146
  end
141
147
 
@@ -0,0 +1,36 @@
1
+ module Avo
2
+ module Concerns
3
+ module HandlesFieldArgs
4
+ extend ActiveSupport::Concern
5
+
6
+ private
7
+
8
+ # Add an instance variable from args
9
+ # That may be a string, boolean, or array
10
+ # Each args should also have a default value
11
+ def add_prop_from_args(args = {}, name: nil, type: :string, default: nil)
12
+ value = default
13
+
14
+ if type == :boolean
15
+ value = args[name.to_sym] == true
16
+ else
17
+ value = args[name.to_sym] unless args.dig(name.to_sym).nil?
18
+ end
19
+
20
+ instance_variable_set(:"@#{name}", value)
21
+ end
22
+
23
+ def add_boolean_prop(args, name, default = false)
24
+ add_prop_from_args args, name: name, default: default, type: :boolean
25
+ end
26
+
27
+ def add_array_prop(args, name, default = [])
28
+ add_prop_from_args args, name: name, default: default, type: :array
29
+ end
30
+
31
+ def add_string_prop(args, name, default = [])
32
+ add_prop_from_args args, name: name, default: default, type: :string
33
+ end
34
+ end
35
+ end
36
+ end
@@ -4,8 +4,9 @@ module Avo
4
4
  extend ActiveSupport::DescendantsTracker
5
5
  extend Avo::Fields::FieldExtensions::HasFieldName
6
6
 
7
- include ActionView::Helpers::UrlHelper
8
7
  include Avo::Fields::FieldExtensions::VisibleInDifferentViews
8
+ include Avo::Concerns::HandlesFieldArgs
9
+ include ActionView::Helpers::UrlHelper
9
10
 
10
11
  delegate :view_context, to: "Avo::App"
11
12
  delegate :main_app, to: :view_context
@@ -0,0 +1,82 @@
1
+ module Avo
2
+ module Fields
3
+ class TagsField < BaseField
4
+ attr_reader :acts_as_taggable_on
5
+ attr_reader :close_on_select
6
+ attr_reader :delimiters
7
+ attr_reader :enforce_suggestions
8
+
9
+ def initialize(id, **args, &block)
10
+ super(id, **args, &block)
11
+
12
+ add_boolean_prop args, :close_on_select
13
+ add_boolean_prop args, :enforce_suggestions
14
+ add_string_prop args, :acts_as_taggable_on
15
+ add_array_prop args, :disallowed
16
+ add_array_prop args, :delimiters, [","]
17
+ add_array_prop args, :suggestions
18
+ end
19
+
20
+ def field_value
21
+ return json_value if acts_as_taggable_on.present?
22
+
23
+ value || []
24
+ end
25
+
26
+ def json_value
27
+ value.map do |item|
28
+ {
29
+ value: item.name
30
+ }
31
+ end.as_json
32
+ end
33
+
34
+ def fill_field(model, key, value, params)
35
+ if acts_as_taggable_on.present?
36
+ model.send(act_as_taggable_attribute(key), parsed_value(value))
37
+ else
38
+ model.send("#{key}=", parsed_value(value))
39
+ end
40
+
41
+ model
42
+ end
43
+
44
+ def suggestions
45
+ return @suggestions if @suggestions.is_a? Array
46
+
47
+ if @suggestions.respond_to? :call
48
+ return Avo::Hosts::RecordHost.new(block: @suggestions, record: model).handle
49
+ end
50
+
51
+ []
52
+ end
53
+
54
+ def disallowed
55
+ return @disallowed if @disallowed.is_a? Array
56
+
57
+ if @disallowed.respond_to? :call
58
+ return Avo::Hosts::RecordHost.new(block: @disallowed, record: model).handle
59
+ end
60
+
61
+ []
62
+ end
63
+
64
+ private
65
+
66
+ def act_as_taggable_attribute(key)
67
+ "#{key.singularize}_list="
68
+ end
69
+
70
+ def parsed_value(value)
71
+ JSON.parse(value).pluck("value")
72
+ rescue
73
+ []
74
+ end
75
+
76
+ private
77
+
78
+ def parse_suggestions_from_args(args)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,7 @@
1
+ module Avo
2
+ module Hosts
3
+ class RecordHost < BaseHost
4
+ option :record
5
+ end
6
+ end
7
+ end
@@ -12,7 +12,8 @@ module Avo
12
12
  :searchable_associations,
13
13
  :resource_ordering,
14
14
  :dashboards,
15
- :menu_editor
15
+ :menu_editor,
16
+ :advanced_fields
16
17
  ]
17
18
  end
18
19
  end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.5.2.pre.5" unless const_defined?(:VERSION)
2
+ VERSION = "2.6.0" unless const_defined?(:VERSION)
3
3
  end
@@ -3,7 +3,7 @@ class <%= class_name.camelize %> < Avo::Dashboards::ChartkickCard
3
3
  self.label = "<%= name.underscore.humanize %>"
4
4
  self.chart_type = :area_chart
5
5
 
6
- query do
6
+ def query
7
7
  data = 3.times.map do |index|
8
8
  {
9
9
  name: "Batch #{index}",
@@ -9,7 +9,7 @@ class <%= class_name.camelize %> < Avo::Dashboards::ChartkickCard
9
9
  # self.chart_options = { library: { plugins: { legend: { display: true } } } }
10
10
  # self.flush = true
11
11
 
12
- query do
12
+ def query
13
13
  points = 16
14
14
  i = Time.new.year.to_i - points
15
15
  base_data = Array.new(points).map do
@@ -2,7 +2,7 @@ class <%= class_name.camelize %> < Avo::Dashboards::MetricCard
2
2
  self.id = "<%= name.underscore %>"
3
3
  self.label = "<%= name.underscore.humanize %>"
4
4
 
5
- query do
5
+ def query
6
6
  result 101
7
7
  end
8
8
  end
@@ -8,7 +8,7 @@ class <%= class_name.camelize %> < Avo::Dashboards::MetricCard
8
8
  # self.prefix = ""
9
9
  # self.suffix = ""
10
10
 
11
- query do
11
+ def query
12
12
  # from = Date.today.midnight - 1.week
13
13
  # to = DateTime.current
14
14
 
@@ -39,6 +39,10 @@ en:
39
39
  zero: 'no %{item}'
40
40
  one: 'one %{item}'
41
41
  other: '%{count} %{item}'
42
+ x_items_more:
43
+ zero: 'no more items'
44
+ one: 'one more item'
45
+ other: '%{count} more items'
42
46
  are_you_sure: 'Are you sure?'
43
47
  filters: 'Filters'
44
48
  per_page: 'Per page'
@@ -5,11 +5,14 @@
5
5
 
6
6
  desc 'Installs Avo assets and bundles them for when you want to use the GitHub repo in your app'
7
7
  task 'avo:build-assets' do
8
+ spec = get_gem_spec 'avo'
9
+ # Uncomment to enable only when the source is github.com
10
+ # enabled = spec.source.to_s.include?('https://github.com/avo-hq/avo')
8
11
  enabled = true
9
12
 
10
13
  if enabled
11
14
  puts "Starting avo:build-assets"
12
- path = locate_gem 'avo'
15
+ path = spec.full_gem_path
13
16
 
14
17
  Dir.chdir(path) do
15
18
  system 'yarn'
@@ -24,11 +27,12 @@ end
24
27
 
25
28
  # From
26
29
  # https://stackoverflow.com/questions/9322078/programmatically-determine-gems-path-using-bundler
27
- def locate_gem(name)
30
+ def get_gem_spec(name)
28
31
  spec = Bundler.load.specs.find{|s| s.name == name }
29
32
  raise GemNotFound, "Could not find gem '#{name}' in the current bundle." unless spec
30
33
  if spec.name == 'bundler'
31
34
  return File.expand_path('../../../', __FILE__)
32
35
  end
33
- spec.full_gem_path
36
+
37
+ spec
34
38
  end