avo 2.10.2 → 2.11.1.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +64 -62
  3. data/app/components/avo/fields/country_field/edit_component.html.erb +9 -3
  4. data/app/components/avo/fields/date_field/edit_component.html.erb +26 -8
  5. data/app/components/avo/fields/date_field/index_component.html.erb +7 -1
  6. data/app/components/avo/fields/date_field/show_component.html.erb +7 -1
  7. data/app/components/avo/fields/date_time_field/edit_component.html.erb +26 -10
  8. data/app/components/avo/fields/date_time_field/index_component.html.erb +10 -1
  9. data/app/components/avo/fields/date_time_field/show_component.html.erb +10 -1
  10. data/app/components/avo/fields/file_field/edit_component.html.erb +1 -0
  11. data/app/components/avo/fields/files_field/edit_component.html.erb +1 -0
  12. data/app/components/avo/fields/select_field/edit_component.html.erb +14 -10
  13. data/app/components/avo/index/ordering/button_component.rb +1 -5
  14. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  15. data/app/controllers/avo/application_controller.rb +15 -9
  16. data/app/controllers/avo/base_controller.rb +11 -3
  17. data/app/controllers/avo/reorder_controller.rb +25 -0
  18. data/app/helpers/avo/application_helper.rb +6 -0
  19. data/app/helpers/avo/url_helpers.rb +0 -4
  20. data/app/javascript/js/controllers/fields/date_field_controller.js +108 -24
  21. data/app/javascript/js/controllers/search_controller.js +3 -0
  22. data/app/views/avo/partials/_javascript.html.erb +1 -1
  23. data/config/routes.rb +5 -4
  24. data/db/factories.rb +1 -0
  25. data/lib/avo/app.rb +1 -3
  26. data/lib/avo/base_action.rb +8 -6
  27. data/lib/avo/base_resource.rb +7 -8
  28. data/lib/avo/concerns/handles_field_args.rb +1 -1
  29. data/lib/avo/concerns/has_fields.rb +2 -0
  30. data/lib/avo/configuration.rb +2 -12
  31. data/lib/avo/fields/base_field.rb +2 -0
  32. data/lib/avo/fields/country_field.rb +2 -0
  33. data/lib/avo/fields/date_field.rb +12 -10
  34. data/lib/avo/fields/date_time_field.rb +21 -9
  35. data/lib/avo/fields/field_extensions/has_include_blank.rb +17 -0
  36. data/lib/avo/fields/file_field.rb +2 -0
  37. data/lib/avo/fields/files_field.rb +2 -0
  38. data/lib/avo/fields/select_field.rb +2 -0
  39. data/lib/avo/licensing/h_q.rb +2 -0
  40. data/lib/avo/services/uri_service.rb +4 -0
  41. data/lib/avo/version.rb +1 -1
  42. data/lib/generators/avo/action_generator.rb +3 -2
  43. data/lib/generators/avo/base_generator.rb +14 -0
  44. data/lib/generators/avo/card/chartkick_generator.rb +18 -0
  45. data/lib/generators/avo/card/metric_generator.rb +18 -0
  46. data/lib/generators/avo/card/partial_generator.rb +19 -0
  47. data/lib/generators/avo/controller_generator.rb +9 -3
  48. data/lib/generators/avo/dashboard_generator.rb +2 -2
  49. data/lib/generators/avo/eject_generator.rb +2 -3
  50. data/lib/generators/avo/field_generator.rb +2 -2
  51. data/lib/generators/avo/filter_generator.rb +3 -2
  52. data/lib/generators/avo/install_generator.rb +2 -2
  53. data/lib/generators/avo/locales_generator.rb +2 -2
  54. data/lib/generators/avo/named_base_generator.rb +14 -0
  55. data/lib/generators/avo/resource_generator.rb +2 -2
  56. data/lib/generators/avo/resource_tool_generator.rb +4 -4
  57. data/lib/generators/avo/tool_generator.rb +4 -4
  58. data/lib/generators/avo/version_generator.rb +23 -0
  59. data/public/avo-assets/avo.css +4 -0
  60. data/public/avo-assets/avo.js +69 -69
  61. data/public/avo-assets/avo.js.map +2 -2
  62. metadata +12 -12
  63. data/db/migrate/20210421064037_add_color_to_teams.rb +0 -5
  64. data/db/migrate/20210423075924_add_progress_to_projects.rb +0 -5
  65. data/db/migrate/20210525143134_add_slug_to_users.rb +0 -6
  66. data/lib/avo/fields/currency_field.rb +0 -15
  67. data/lib/avo/has_context.rb +0 -7
  68. data/lib/generators/avo/chartkick_card_generator.rb +0 -16
  69. data/lib/generators/avo/metric_card_generator.rb +0 -16
  70. data/lib/generators/avo/partial_card_generator.rb +0 -17
@@ -2,63 +2,147 @@ import { Controller } from '@hotwired/stimulus'
2
2
  import { DateTime } from 'luxon'
3
3
  import flatpickr from 'flatpickr'
4
4
 
5
- import { castBoolean } from '../../helpers/cast_boolean'
6
-
7
5
  // Get the DateTime with the TZ offset applied.
8
6
  function universalTimestamp(timestampStr) {
9
7
  return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000))
10
8
  }
11
9
 
12
10
  export default class extends Controller {
13
- static targets = ['input']
11
+ static targets = ['input', 'fakeInput']
12
+
13
+ static values = {
14
+ view: String,
15
+ timezone: String,
16
+ format: String,
17
+ enableTime: Boolean,
18
+ pickerFormat: String,
19
+ firstDayOfWeek: Number,
20
+ time24Hr: Boolean,
21
+ disableMobile: Boolean,
22
+ }
23
+
24
+ get browserZone() {
25
+ const time = DateTime.local()
26
+
27
+ return time.zoneName
28
+ }
29
+
30
+ get initialValue() {
31
+ if (this.isOnShow || this.isOnIndex) {
32
+ return this.context.element.innerText
33
+ } if (this.isOnEdit) {
34
+ return this.inputTarget.value
35
+ }
36
+
37
+ return null
38
+ }
39
+
40
+ get isOnIndex() {
41
+ return this.viewValue === 'index'
42
+ }
43
+
44
+ get isOnEdit() {
45
+ return this.viewValue === 'edit'
46
+ }
47
+
48
+ get isOnShow() {
49
+ return this.viewValue === 'show'
50
+ }
51
+
52
+ // Parse the time as if it were UTC
53
+ get parsedValue() {
54
+ return DateTime.fromISO(this.initialValue, { zone: 'UTC' })
55
+ }
56
+
57
+ get displayTimezone() {
58
+ return this.timezoneValue || this.browserZone
59
+ }
14
60
 
15
61
  connect() {
62
+ if (this.isOnShow || this.isOnIndex) {
63
+ this.initShow()
64
+ } else if (this.isOnEdit) {
65
+ this.initEdit()
66
+ }
67
+ }
68
+
69
+ // Turns the value in the controller wrapper into the timezone of the browser
70
+ initShow() {
71
+ let value = this.parsedValue
72
+
73
+ // Set the zone only if the type of field is date time.
74
+ if (this.enableTimeValue) {
75
+ value = value.setZone(this.displayTimezone)
76
+ }
77
+
78
+ this.context.element.innerText = value.toFormat(this.formatValue)
79
+ }
80
+
81
+ initEdit() {
16
82
  const options = {
17
83
  enableTime: false,
18
84
  enableSeconds: false,
19
85
  // eslint-disable-next-line camelcase
20
- time_24hr: false,
86
+ time_24hr: this.time24HrValue,
21
87
  locale: {
22
88
  firstDayOfWeek: 0,
23
89
  },
24
90
  altInput: true,
91
+ onChange: this.onChange.bind(this),
25
92
  }
26
- const enableTime = castBoolean(this.inputTarget.dataset.enableTime)
27
93
 
28
94
  // Set the format of the displayed input field.
29
- options.altFormat = this.inputTarget.dataset.pickerFormat
95
+ options.altFormat = this.pickerFormatValue
30
96
 
31
97
  // Disable native input in mobile browsers
32
- options.disableMobile = this.inputTarget.dataset.disableMobile
98
+ options.disableMobile = this.disableMobileValue
33
99
 
34
100
  // Set first day of the week.
35
- options.locale.firstDayOfWeek = this.inputTarget.dataset.firstDayOfWeek
101
+ options.locale.firstDayOfWeek = this.firstDayOfWeekValue
36
102
 
37
103
  // Enable time if needed.
38
- options.enableTime = enableTime
39
- options.enableSeconds = enableTime
40
-
41
- let currentValue
104
+ options.enableTime = this.enableTimeValue
105
+ options.enableSeconds = this.enableTimeValue
42
106
 
43
107
  // enable timezone display
44
- if (enableTime) {
45
- currentValue = DateTime.fromISO(this.inputTarget.value, { zone: window.Avo.configuration.timezone })
46
- currentValue = currentValue.setZone(this.inputTarget.dataset.timezone)
47
- currentValue = currentValue.toISO()
108
+ if (this.enableTimeValue) {
109
+ console.log(1)
110
+ options.defaultDate = this.parsedValue.setZone(this.displayTimezone).toISO()
48
111
 
49
112
  options.dateFormat = 'Y-m-d H:i:S'
50
- // eslint-disable-next-line camelcase
51
- options.time_24hr = castBoolean(this.inputTarget.dataset.time24hr)
52
- // this.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
53
- options.appTimezone = this.inputTarget.dataset.timezone
54
113
  } else {
55
- // Because the browser treats the date like a timestamp and updates it ot 00:00 hour, when on a western timezone the date will be converted with one day offset.
114
+ console.log(2)
115
+
116
+ // Because the browser treats the date like a timestamp and updates it at 00:00 hour, when on a western timezone the date will be converted with one day offset.
56
117
  // Ex: 2022-01-30 will render as 2022-01-29 on an American timezone
57
- currentValue = universalTimestamp(this.inputTarget.value)
118
+ options.defaultDate = universalTimestamp(this.initialValue)
58
119
  }
59
120
 
60
- options.defaultDate = currentValue
121
+ flatpickr(this.fakeInputTarget, options)
122
+
123
+ this.updateRealInput(this.parsedValue.setZone(this.displayTimezone).toISO())
124
+ }
125
+
126
+ onChange(selectedDates) {
127
+ let time
128
+ let args = {}
129
+
130
+ if (this.timezoneValue) {
131
+ args = { keepLocalTime: true }
132
+ } else {
133
+ args = { keepLocalTime: false }
134
+ }
135
+
136
+ if (this.enableTimeValue) {
137
+ time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', args)
138
+ } else {
139
+ time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: true })
140
+ }
141
+
142
+ this.updateRealInput(time)
143
+ }
61
144
 
62
- flatpickr(this.inputTarget, options)
145
+ updateRealInput(value) {
146
+ this.inputTarget.value = value
63
147
  }
64
148
  }
@@ -147,6 +147,9 @@ export default class extends Controller {
147
147
  case 'rounded':
148
148
  classes = 'rounded'
149
149
  break
150
+ case 'square':
151
+ classes = 'rounded-none'
152
+ break
150
153
  }
151
154
 
152
155
  children.push(
@@ -1,6 +1,6 @@
1
1
  <%= javascript_tag nonce: true do %>
2
2
  window.Avo = window.Avo || { configuration: {} }
3
3
  Avo.configuration.timezone = '<%= Avo.configuration.timezone %>'
4
- Avo.configuration.root_path = '<%= Avo.configuration.root_path %>'
4
+ Avo.configuration.root_path = '<%= root_path_without_url %>'
5
5
  Avo.configuration.search_debounce = '<%= Avo.configuration.search_debounce %>'
6
6
  <% end %>
data/config/routes.rb CHANGED
@@ -16,16 +16,17 @@ Avo::Engine.routes.draw do
16
16
  post "/resources/:resource_name/:id/attachments/", to: "attachments#create"
17
17
  end
18
18
 
19
+ # Records ordering
20
+ scope "reorder", as: "reorder" do
21
+ patch "/:resource_name/:id", to: "reorder#order", as: "order"
22
+ end
23
+
19
24
  get "failed_to_load", to: "home#failed_to_load"
20
25
 
21
26
  scope "resources", as: "resources" do
22
27
  # Attachments
23
28
  delete "/:resource_name/:id/active_storage_attachments/:attachment_name/:attachment_id", to: "attachments#destroy"
24
29
 
25
- # Ordering
26
- patch "/:resource_name/:id/order", to: "resources#order", as: "order"
27
- patch "/:resource_name/:id/:related_name/:related_id/order", to: "associations#order", as: "associations_order"
28
-
29
30
  # Actions
30
31
  get "/:resource_name(/:id)/actions/:action_id", to: "actions#show"
31
32
  post "/:resource_name(/:id)/actions/:action_id", to: "actions#handle"
data/db/factories.rb CHANGED
@@ -44,6 +44,7 @@ FactoryBot.define do
44
44
 
45
45
  factory :comment do
46
46
  body { Faker::Lorem.paragraphs(number: rand(4...10)).join(" ") }
47
+ posted_at { Time.now - rand(10...365).days }
47
48
  end
48
49
 
49
50
  factory :review do
data/lib/avo/app.rb CHANGED
@@ -20,8 +20,6 @@ module Avo
20
20
  def boot
21
21
  init_fields
22
22
 
23
- I18n.locale = Avo.configuration.language_code
24
-
25
23
  if Rails.cache.instance_of?(ActiveSupport::Cache::NullStore)
26
24
  self.cache_store ||= ActiveSupport::Cache::MemoryStore.new
27
25
  else
@@ -172,7 +170,7 @@ module Avo
172
170
  payload[:license_abilities] = Avo::App&.license&.abilities
173
171
  payload[:cache_store] = self.cache_store&.class&.to_s
174
172
  payload[:avo_metadata] = hq&.avo_metadata
175
- payload[:app_timezone] = Time.now.zone
173
+ payload[:app_timezone] = Time.current.zone
176
174
  payload[:cache_key] = Avo::Licensing::HQ.cache_key
177
175
  payload[:cache_key_contents] = hq&.cached_response
178
176
 
@@ -1,7 +1,5 @@
1
1
  module Avo
2
2
  class BaseAction
3
- extend HasContext
4
-
5
3
  include Avo::Concerns::HasFields
6
4
 
7
5
  class_attribute :name, default: nil
@@ -23,8 +21,16 @@ module Avo
23
21
  attr_accessor :user
24
22
 
25
23
  delegate :view, to: :class
24
+ delegate :context, to: ::Avo::App
25
+ delegate :current_user, to: ::Avo::App
26
+ delegate :params, to: ::Avo::App
27
+ delegate :view_context, to: ::Avo::App
28
+ delegate :avo, to: :view_context
29
+ delegate :main_app, to: :view_context
26
30
 
27
31
  class << self
32
+ delegate :context, to: ::Avo::App
33
+
28
34
  def form_data_attributes
29
35
  # We can't respond with a file download from Turbo se we disable it on the form
30
36
  if may_download_file
@@ -64,10 +70,6 @@ module Avo
64
70
  @response[:messages] = []
65
71
  end
66
72
 
67
- def context
68
- self.class.context
69
- end
70
-
71
73
  def get_attributes_for_action
72
74
  get_fields.map do |field|
73
75
  [field.id, field.value]
@@ -1,7 +1,6 @@
1
1
  module Avo
2
2
  class BaseResource
3
3
  extend ActiveSupport::DescendantsTracker
4
- extend HasContext
5
4
 
6
5
  include ActionView::Helpers::UrlHelper
7
6
  include Avo::Concerns::HasModel
@@ -10,12 +9,15 @@ module Avo
10
9
  include Avo::Concerns::ModelClassConstantized
11
10
 
12
11
  delegate :view_context, to: ::Avo::App
12
+ delegate :current_user, to: ::Avo::App
13
+ delegate :params, to: ::Avo::App
13
14
  delegate :simple_format, :content_tag, to: :view_context
14
15
  delegate :main_app, to: :view_context
15
16
  delegate :avo, to: :view_context
16
17
  delegate :resource_path, to: :view_context
17
18
  delegate :resources_path, to: :view_context
18
19
  delegate :t, to: ::I18n
20
+ delegate :context, to: ::Avo::App
19
21
 
20
22
  attr_accessor :view
21
23
  attr_accessor :model
@@ -48,6 +50,7 @@ module Avo
48
50
 
49
51
  class << self
50
52
  delegate :t, to: ::I18n
53
+ delegate :context, to: ::Avo::App
51
54
 
52
55
  def grid(&block)
53
56
  grid_collector = GridCollector.new
@@ -101,7 +104,7 @@ module Avo
101
104
 
102
105
  def initialize
103
106
  unless self.class.model_class.present?
104
- if model_class.present?
107
+ if model_class.present? && model_class.respond_to?(:base_class)
105
108
  self.class.model_class = model_class.base_class
106
109
  end
107
110
  end
@@ -153,7 +156,7 @@ module Avo
153
156
  end
154
157
 
155
158
  def class_name_without_resource
156
- self.class.name.demodulize.chomp("Resource")
159
+ self.class.name.demodulize.delete_suffix("Resource")
157
160
  end
158
161
 
159
162
  def model_class
@@ -199,7 +202,7 @@ module Avo
199
202
  end
200
203
 
201
204
  def name
202
- default = class_name_without_resource.titlecase
205
+ default = class_name_without_resource.to_s.gsub('::', ' ').underscore.humanize
203
206
 
204
207
  return @name if @name.present?
205
208
 
@@ -242,10 +245,6 @@ module Avo
242
245
  view_types
243
246
  end
244
247
 
245
- def context
246
- self.class.context
247
- end
248
-
249
248
  def attached_file_fields
250
249
  get_field_definitions.select do |field|
251
250
  [Avo::Fields::FileField, Avo::Fields::FilesField].include? field.class
@@ -28,7 +28,7 @@ module Avo
28
28
  add_prop_from_args args, name: name, default: default, type: :array
29
29
  end
30
30
 
31
- def add_string_prop(args, name, default = [])
31
+ def add_string_prop(args, name, default = nil)
32
32
  add_prop_from_args args, name: name, default: default, type: :string
33
33
  end
34
34
  end
@@ -183,6 +183,7 @@ module Avo
183
183
  if field.is_a?(Avo::Fields::BelongsToField)
184
184
  if field.respond_to?(:foreign_key) &&
185
185
  reflection.inverse_of.present? &&
186
+ reflection.inverse_of.respond_to?(:foreign_key) &&
186
187
  reflection.inverse_of.foreign_key == field.foreign_key
187
188
  is_valid = false
188
189
  end
@@ -191,6 +192,7 @@ module Avo
191
192
  if field.respond_to?(:foreign_key) &&
192
193
  field.is_polymorphic? &&
193
194
  reflection.respond_to?(:polymorphic?) &&
195
+ reflection.inverse_of.respond_to?(:foreign_key) &&
194
196
  reflection.inverse_of.foreign_key == field.reflection.foreign_key
195
197
  is_valid = false
196
198
  end
@@ -40,7 +40,7 @@ module Avo
40
40
  @per_page = 24
41
41
  @per_page_steps = [12, 24, 48, 72]
42
42
  @via_per_page = 8
43
- @locale = "en-US"
43
+ @locale = nil
44
44
  @currency = "USD"
45
45
  @default_view_type = :table
46
46
  @license = "community"
@@ -78,16 +78,6 @@ module Avo
78
78
  @profile_menu = nil
79
79
  end
80
80
 
81
- def locale_tag
82
- ::ISO::Tag.new(locale)
83
- end
84
-
85
- def language_code
86
- locale_tag.language.code
87
- rescue
88
- "en"
89
- end
90
-
91
81
  def current_user_method(&block)
92
82
  @current_user = block if block.present?
93
83
  end
@@ -123,7 +113,7 @@ module Avo
123
113
  end
124
114
 
125
115
  def computed_root_path
126
- Avo::App.root_path
116
+ Avo.configuration.root_path
127
117
  end
128
118
 
129
119
  def feature_enabled?(feature)
@@ -76,6 +76,8 @@ module Avo
76
76
  @index_text_align = args[:index_text_align] || :left
77
77
  @html = args[:html] || nil
78
78
 
79
+ @args = args
80
+
79
81
  @updatable = true
80
82
  @computable = true
81
83
  @computed = block.present?
@@ -1,6 +1,8 @@
1
1
  module Avo
2
2
  module Fields
3
3
  class CountryField < BaseField
4
+ include Avo::Fields::FieldExtensions::HasIncludeBlank
5
+
4
6
  attr_reader :countries
5
7
  attr_reader :display_code
6
8
 
@@ -10,21 +10,23 @@ module Avo
10
10
  def initialize(id, **args, &block)
11
11
  super(id, **args, &block)
12
12
 
13
- @first_day_of_week = args[:first_day_of_week].present? ? args[:first_day_of_week].to_i : 0
14
- @picker_format = args[:picker_format].present? ? args[:picker_format] : "Y-m-d"
15
- @format = args[:format].present? ? args[:format] : :long
16
- @relative = args[:relative].present? ? args[:relative] : false
17
- @disable_mobile = args[:disable_mobile].present? ? args[:disable_mobile] : false
13
+ add_string_prop args, :first_day_of_week, 0
14
+ add_string_prop args, :picker_format, "Y-m-d"
15
+ add_string_prop args, :format, "yyyy-LL-dd"
16
+ add_boolean_prop args, :relative
17
+ add_boolean_prop args, :disable_mobile
18
18
  end
19
19
 
20
20
  def formatted_value
21
21
  return if value.blank?
22
22
 
23
- if @format.is_a?(Symbol)
24
- value.to_formatted_s(@format)
25
- else
26
- value.strftime(@format)
27
- end
23
+ value.iso8601
24
+ end
25
+
26
+ def edit_formatted_value
27
+ return nil if value.nil?
28
+
29
+ value.iso8601
28
30
  end
29
31
  end
30
32
  end
@@ -2,25 +2,29 @@ module Avo
2
2
  module Fields
3
3
  class DateTimeField < DateField
4
4
  attr_reader :format
5
+ attr_reader :picker_format
5
6
  attr_reader :time_24hr
6
7
  attr_reader :timezone
7
8
 
8
9
  def initialize(id, **args, &block)
9
10
  super(id, **args, &block)
10
11
 
11
- @picker_format = args[:picker_format].present? ? args[:picker_format] : "Y-m-d H:i:S"
12
- @time_24hr = args[:time_24hr].present? ? args[:time_24hr] : false
13
- @timezone = args[:timezone].present? ? args[:timezone] : Rails.application.config.time_zone
12
+ add_boolean_prop args, :time_24hr
13
+ add_string_prop args, :picker_format, "Y-m-d H:i:S"
14
+ add_string_prop args, :format, "yyyy-LL-dd TT"
15
+ add_string_prop args, :timezone
14
16
  end
15
17
 
16
18
  def formatted_value
17
19
  return nil if value.nil?
18
20
 
19
- if @format.is_a?(Symbol)
20
- value.to_time.in_time_zone(timezone).to_formatted_s(@format)
21
- else
22
- value.to_time.in_time_zone(timezone).strftime(@format)
23
- end
21
+ value.utc.to_time.iso8601
22
+ end
23
+
24
+ def edit_formatted_value
25
+ return nil if value.nil?
26
+
27
+ value.utc.iso8601
24
28
  end
25
29
 
26
30
  def fill_field(model, key, value, params)
@@ -32,10 +36,18 @@ module Avo
32
36
 
33
37
  return model if value.blank?
34
38
 
35
- model[id] = value.to_time.in_time_zone(Rails.application.config.time_zone)
39
+ model[id] = utc_time(value)
36
40
 
37
41
  model
38
42
  end
43
+
44
+ def utc_time(value)
45
+ if timezone.present?
46
+ ActiveSupport::TimeZone.new(timezone).local_to_utc(Time.parse(value))
47
+ else
48
+ value
49
+ end
50
+ end
39
51
  end
40
52
  end
41
53
  end
@@ -0,0 +1,17 @@
1
+ module Avo
2
+ module Fields
3
+ module FieldExtensions
4
+ module HasIncludeBlank
5
+ def include_blank
6
+ if @args[:include_blank] == true
7
+ placeholder || '—'
8
+ elsif @args[:include_blank] == false
9
+ false
10
+ else
11
+ @args[:include_blank]
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -6,6 +6,7 @@ module Avo
6
6
  attr_accessor :is_image
7
7
  attr_accessor :is_audio
8
8
  attr_accessor :direct_upload
9
+ attr_accessor :accept
9
10
 
10
11
  def initialize(id, **args, &block)
11
12
  super(id, **args, &block)
@@ -15,6 +16,7 @@ module Avo
15
16
  @is_image = args[:is_image].present? ? args[:is_image] : @is_avatar
16
17
  @is_audio = args[:is_audio].present? ? args[:is_audio] : false
17
18
  @direct_upload = args[:direct_upload].present? ? args[:direct_upload] : false
19
+ @accept = args[:accept].present? ? args[:accept] : nil
18
20
  end
19
21
 
20
22
  def path
@@ -4,6 +4,7 @@ module Avo
4
4
  attr_accessor :is_audio
5
5
  attr_accessor :is_image
6
6
  attr_accessor :direct_upload
7
+ attr_accessor :accept
7
8
 
8
9
  def initialize(id, **args, &block)
9
10
  super(id, **args, &block)
@@ -11,6 +12,7 @@ module Avo
11
12
  @is_audio = args[:is_audio].present? ? args[:is_audio] : false
12
13
  @is_image = args[:is_image].present? ? args[:is_image] : @is_avatar
13
14
  @direct_upload = args[:direct_upload].present? ? args[:direct_upload] : false
15
+ @accept = args[:accept].present? ? args[:accept] : nil
14
16
  end
15
17
 
16
18
  def view_component_name
@@ -1,6 +1,8 @@
1
1
  module Avo
2
2
  module Fields
3
3
  class SelectField < BaseField
4
+ include Avo::Fields::FieldExtensions::HasIncludeBlank
5
+
4
6
  attr_reader :options
5
7
  attr_reader :enum
6
8
  attr_reader :display_value
@@ -121,6 +121,8 @@ module Avo
121
121
  cache_and_return_error "HTTP connection reset error.", exception.message
122
122
  rescue Errno::ECONNREFUSED => exception
123
123
  cache_and_return_error "HTTP connection refused error.", exception.message
124
+ rescue OpenSSL::SSL::SSLError => exception
125
+ cache_and_return_error "OpenSSL error.", exception.message
124
126
  rescue HTTParty::Error => exception
125
127
  cache_and_return_error "HTTP client error.", exception.message
126
128
  rescue Net::OpenTimeout => exception
@@ -13,6 +13,10 @@ module Avo
13
13
  @uri = Addressable::URI.parse(path)
14
14
  end
15
15
 
16
+ def call
17
+ to_s
18
+ end
19
+
16
20
  def append_paths(*paths)
17
21
  paths = Array.wrap(paths).flatten
18
22
 
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.10.2" unless const_defined?(:VERSION)
2
+ VERSION = "2.11.1.pre.1" unless const_defined?(:VERSION)
3
3
  end
@@ -1,9 +1,10 @@
1
- require "rails/generators"
1
+ require_relative "named_base_generator"
2
2
 
3
3
  module Generators
4
4
  module Avo
5
- class ActionGenerator < Rails::Generators::NamedBase
5
+ class ActionGenerator < NamedBaseGenerator
6
6
  source_root File.expand_path("templates", __dir__)
7
+
7
8
  class_option :standalone, type: :boolean
8
9
 
9
10
  namespace "avo:action"
@@ -0,0 +1,14 @@
1
+ require "rails/generators"
2
+
3
+ module Generators
4
+ module Avo
5
+ class BaseGenerator < ::Rails::Generators::Base
6
+ hide!
7
+
8
+ def initialize(*args)
9
+ super(*args)
10
+ invoke "avo:version", *args
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ require_relative "../named_base_generator"
2
+
3
+ module Generators
4
+ module Avo
5
+ module Card
6
+ class ChartkickGenerator < Generators::Avo::NamedBaseGenerator
7
+ source_root File.expand_path("../templates", __dir__)
8
+
9
+ namespace "avo:card:chartkick"
10
+ desc "Add a chartkick card for your Avo dashboard."
11
+
12
+ def handle
13
+ template "cards/chartkick_card_sample.tt", "app/avo/cards/#{name.underscore}.rb"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end