avo 2.9.1.pre7 → 2.9.2.pre1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/builds/action_cable.js +2 -0
  4. data/app/assets/builds/action_cable.js.map +7 -0
  5. data/app/assets/builds/application.js +2 -0
  6. data/app/assets/builds/application.js.map +7 -0
  7. data/app/assets/builds/avo.css +9028 -0
  8. data/app/assets/builds/avo.js +512 -0
  9. data/app/assets/builds/avo.js.map +7 -0
  10. data/app/assets/builds/avo_custom.js +6 -0
  11. data/app/assets/builds/avo_custom.js.map +7 -0
  12. data/app/components/avo/actions_component.rb +2 -6
  13. data/app/components/avo/fields/common/key_value_component.html.erb +2 -2
  14. data/app/components/avo/fields/common/single_file_viewer_component.rb +1 -1
  15. data/app/components/avo/fields/date_field/edit_component.html.erb +0 -1
  16. data/app/components/avo/fields/date_time_field/edit_component.html.erb +25 -10
  17. data/app/components/avo/fields/date_time_field/index_component.html.erb +9 -1
  18. data/app/components/avo/fields/date_time_field/show_component.html.erb +9 -1
  19. data/app/components/avo/index/ordering/button_component.rb +13 -5
  20. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  21. data/app/components/avo/index/resource_controls_component.rb +1 -5
  22. data/app/components/avo/views/resource_edit_component.rb +1 -1
  23. data/app/components/avo/views/resource_index_component.rb +2 -2
  24. data/app/controllers/avo/application_controller.rb +3 -24
  25. data/app/javascript/avo.js +1 -5
  26. data/app/javascript/js/controllers/fields/date_field_controller.js +87 -25
  27. data/app/views/avo/partials/_javascript.html.erb +1 -1
  28. data/config/routes.rb +1 -1
  29. data/lib/avo/app.rb +4 -11
  30. data/lib/avo/base_card.rb +7 -1
  31. data/lib/avo/base_resource.rb +1 -1
  32. data/lib/avo/concerns/handles_field_args.rb +1 -1
  33. data/lib/avo/dashboards/base_dashboard.rb +1 -1
  34. data/lib/avo/fields/date_field.rb +0 -2
  35. data/lib/avo/fields/date_time_field.rb +21 -9
  36. data/lib/avo/fields/has_base_field.rb +1 -3
  37. data/lib/avo/fields/has_one_field.rb +1 -4
  38. data/lib/avo/menu/builder.rb +7 -8
  39. data/lib/avo/version.rb +1 -1
  40. data/lib/avo.rb +0 -1
  41. data/public/avo-assets/avo.js +68 -68
  42. data/public/avo-assets/avo.js.map +2 -2
  43. metadata +11 -9
  44. data/app/javascript/js/controllers/tabs_controller.js +0 -80
  45. data/db/migrate/20210421064037_add_color_to_teams.rb +0 -5
  46. data/db/migrate/20210423075924_add_progress_to_projects.rb +0 -5
  47. data/db/migrate/20210525143134_add_slug_to_users.rb +0 -6
  48. data/lib/avo/concerns/model_class_constantized.rb +0 -23
  49. data/lib/avo/services/uri_service.rb +0 -71
  50. data/lib/generators/avo/templates/locales/avo.fr.yml +0 -115
@@ -13,8 +13,7 @@ module Avo
13
13
  protect_from_forgery with: :exception
14
14
  before_action :init_app
15
15
  before_action :check_avo_license
16
- before_action :set_default_locale
17
- around_action :set_force_locale
16
+ before_action :set_locale
18
17
  before_action :set_authorization
19
18
  before_action :_authenticate!
20
19
  before_action :set_container_classes
@@ -28,7 +27,7 @@ module Avo
28
27
  add_flash_types :info, :warning, :success, :error
29
28
 
30
29
  def init_app
31
- Avo::App.init request: request, context: context, current_user: _current_user, view_context: view_context, params: params
30
+ Avo::App.init request: request, context: context, root_path: avo.root_path.delete_suffix("/"), current_user: _current_user, view_context: view_context, params: params
32
31
 
33
32
  @license = Avo::App.license
34
33
  end
@@ -288,30 +287,10 @@ module Avo
288
287
  @resource.form_scope
289
288
  end
290
289
 
291
- def set_default_locale
290
+ def set_locale
292
291
  I18n.locale = params[:set_locale] || I18n.default_locale
293
292
 
294
293
  I18n.default_locale = I18n.locale
295
294
  end
296
-
297
- # Temporary set the locale
298
- def set_force_locale
299
- if params[:force_locale].present?
300
- initial_locale = I18n.locale.to_s.dup
301
- I18n.locale = params[:force_locale]
302
- yield
303
- I18n.locale = initial_locale
304
- else
305
- yield
306
- end
307
- end
308
-
309
- def default_url_options
310
- if params[:force_locale].present?
311
- { **super, force_locale: params[:force_locale] }
312
- else
313
- super
314
- end
315
- end
316
295
  end
317
296
  end
@@ -74,11 +74,7 @@ document.addEventListener('turbo:frame-load', () => {
74
74
  document.addEventListener('turbo:before-fetch-response', async (e) => {
75
75
  if (e.detail.fetchResponse.response.status === 500) {
76
76
  const { id, src } = e.target
77
- // Don't try to redirect to failed to load if this is alread a redirection to failed to load and crashed somewhere.
78
- // You'll end up with a request loop.
79
- if (!e.detail.fetchResponse?.response?.url?.includes('/failed_to_load')) {
80
- e.target.src = `${window.Avo.configuration.root_path}/failed_to_load?turbo_frame=${id}&src=${src}`
81
- }
77
+ e.target.src = `${window.Avo.configuration.root_path}/failed_to_load?turbo_frame=${id}&src=${src}`
82
78
  }
83
79
  })
84
80
 
@@ -2,63 +2,125 @@ 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
+ }
22
+
23
+ get browserZone() {
24
+ const time = DateTime.local()
25
+
26
+ return time.zoneName
27
+ }
28
+
29
+ get initialValue() {
30
+ if (this.isOnShow || this.isOnIndex) {
31
+ return this.context.element.innerText
32
+ } if (this.isOnEdit) {
33
+ return this.inputTarget.value
34
+ }
35
+
36
+ return null
37
+ }
38
+
39
+ get isOnIndex() {
40
+ return this.viewValue === 'index'
41
+ }
42
+
43
+ get isOnEdit() {
44
+ return this.viewValue === 'edit'
45
+ }
46
+
47
+ get isOnShow() {
48
+ return this.viewValue === 'show'
49
+ }
50
+
51
+ // Parse the time as if it were UTC
52
+ get parsedValue() {
53
+ return DateTime.fromISO(this.initialValue, { zone: 'UTC' })
54
+ }
55
+
56
+ get displayTimezone() {
57
+ return this.timezoneValue || this.browserZone
58
+ }
14
59
 
15
60
  connect() {
61
+ if (this.isOnShow || this.isOnIndex) {
62
+ this.initShow()
63
+ } else if (this.isOnEdit) {
64
+ this.initEdit()
65
+ }
66
+ }
67
+
68
+ // Turns the value in the controller wrapper into the timezone of the browser
69
+ initShow() {
70
+ this.context.element.innerText = this.parsedValue.setZone(this.displayTimezone).toFormat(this.formatValue)
71
+ }
72
+
73
+ initEdit() {
16
74
  const options = {
17
75
  enableTime: false,
18
76
  enableSeconds: false,
19
77
  // eslint-disable-next-line camelcase
20
- time_24hr: false,
78
+ time_24hr: this.time24HrValue,
21
79
  locale: {
22
80
  firstDayOfWeek: 0,
23
81
  },
24
82
  altInput: true,
83
+ onChange: this.onChange.bind(this),
25
84
  }
26
- const enableTime = castBoolean(this.inputTarget.dataset.enableTime)
27
85
 
28
86
  // Set the format of the displayed input field.
29
- options.altFormat = this.inputTarget.dataset.pickerFormat
30
-
31
- // Disable native input in mobile browsers
32
- options.disableMobile = this.inputTarget.dataset.disableMobile
87
+ options.altFormat = this.pickerFormatValue
33
88
 
34
89
  // Set first day of the week.
35
- options.locale.firstDayOfWeek = this.inputTarget.dataset.firstDayOfWeek
90
+ options.locale.firstDayOfWeek = this.firstDayOfWeekValue
36
91
 
37
92
  // Enable time if needed.
38
- options.enableTime = enableTime
39
- options.enableSeconds = enableTime
40
-
41
- let currentValue
93
+ options.enableTime = this.enableTimeValue
94
+ options.enableSeconds = this.enableTimeValue
42
95
 
43
96
  // 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()
97
+ if (this.enableTimeValue) {
98
+ options.defaultDate = this.parsedValue.setZone(this.displayTimezone).toISO()
48
99
 
49
100
  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
101
  } else {
55
102
  // 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.
56
103
  // Ex: 2022-01-30 will render as 2022-01-29 on an American timezone
57
- currentValue = universalTimestamp(this.inputTarget.value)
104
+ options.defaultDate = universalTimestamp(this.initialValue)
58
105
  }
59
106
 
60
- options.defaultDate = currentValue
107
+ flatpickr(this.fakeInputTarget, options)
108
+
109
+ this.updateRealInput(this.parsedValue.setZone(this.displayTimezone).toISO())
110
+ }
111
+
112
+ onChange(selectedDates) {
113
+ let time
114
+
115
+ if (this.timezoneValue) {
116
+ time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: true })
117
+ } else {
118
+ time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: false })
119
+ }
120
+ this.updateRealInput(time)
121
+ }
61
122
 
62
- flatpickr(this.inputTarget, options)
123
+ updateRealInput(value) {
124
+ this.inputTarget.value = value
63
125
  }
64
126
  }
@@ -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 = '<%= Avo::App.root_path %>'
5
5
  Avo.configuration.search_debounce = '<%= Avo.configuration.search_debounce %>'
6
6
  <% end %>
data/config/routes.rb CHANGED
@@ -23,7 +23,7 @@ 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
  # Ordering
26
- patch "/:resource_name/:id/order", to: "resources#order", as: "order"
26
+ patch "/:resource_name/:id/order", to: "resources#order"
27
27
  patch "/:resource_name/:id/:related_name/:related_id/order", to: "associations#order", as: "associations_order"
28
28
 
29
29
  # Actions
data/lib/avo/app.rb CHANGED
@@ -29,21 +29,14 @@ module Avo
29
29
  end
30
30
  end
31
31
 
32
- # Renerate a dynamic root path using the URIService
33
- def root_path(paths: [], query: {}, **args)
34
- Avo::Services::URIService.parse(view_context.avo.root_url.to_s)
35
- .append_paths(paths)
36
- .append_query(query)
37
- .to_s
38
- end
39
-
40
- def init(request:, context:, current_user:, view_context:, params:)
32
+ def init(request:, context:, current_user:, root_path:, view_context:, params:)
41
33
  self.error_messages = []
34
+ self.request = request
42
35
  self.context = context
43
36
  self.current_user = current_user
44
- self.params = params
45
- self.request = request
37
+ self.root_path = root_path
46
38
  self.view_context = view_context
39
+ self.params = params
47
40
 
48
41
  self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
49
42
  self.translation_enabled = license.has(:localization)
data/lib/avo/base_card.rb CHANGED
@@ -59,7 +59,13 @@ module Avo
59
59
  def frame_url(enforced_range: nil, params: {})
60
60
  enforced_range ||= initial_range || ranges.first
61
61
 
62
- Avo::App.view_context.avo.dashboard_card_path(dashboard.id, id, turbo_frame: turbo_frame, index: index, range: enforced_range, **params.permit!)
62
+ # append the parent params to the card request
63
+ begin
64
+ other_params = "&#{params.permit!.to_h.map { |k, v| "#{k}=#{v}" }.join("&")}"
65
+ rescue
66
+ end
67
+
68
+ "#{Avo::App.root_path}/dashboards/#{dashboard.id}/cards/#{id}?turbo_frame=#{turbo_frame}&index=#{index}&range=#{enforced_range}#{other_params}"
63
69
  end
64
70
 
65
71
  def card_classes
@@ -8,7 +8,6 @@ module Avo
8
8
  include Avo::Concerns::HasModel
9
9
  include Avo::Concerns::HasFields
10
10
  include Avo::Concerns::HasStimulusControllers
11
- include Avo::Concerns::ModelClassConstantized
12
11
 
13
12
  delegate :view_context, to: ::Avo::App
14
13
  delegate :simple_format, :content_tag, to: :view_context
@@ -30,6 +29,7 @@ module Avo
30
29
  class_attribute :search_query, default: nil
31
30
  class_attribute :search_query_help, default: ""
32
31
  class_attribute :includes, default: []
32
+ class_attribute :model_class
33
33
  class_attribute :translation_key
34
34
  class_attribute :default_view_type, default: :table
35
35
  class_attribute :devise_password_optional, default: false
@@ -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
@@ -65,7 +65,7 @@ module Avo
65
65
  end
66
66
 
67
67
  def navigation_path
68
- Avo::App.view_context.avo.dashboard_path id
68
+ "#{Avo::App.root_path}/dashboards/#{id}"
69
69
  end
70
70
 
71
71
  def is_visible?
@@ -3,7 +3,6 @@ module Avo
3
3
  class DateField < TextField
4
4
  attr_reader :first_day_of_week
5
5
  attr_reader :picker_format
6
- attr_reader :disable_mobile
7
6
  attr_reader :format
8
7
  attr_reader :relative
9
8
 
@@ -14,7 +13,6 @@ module Avo
14
13
  @picker_format = args[:picker_format].present? ? args[:picker_format] : "Y-m-d"
15
14
  @format = args[:format].present? ? args[:format] : :long
16
15
  @relative = args[:relative].present? ? args[:relative] : false
17
- @disable_mobile = args[:disable_mobile].present? ? args[:disable_mobile] : false
18
16
  end
19
17
 
20
18
  def formatted_value
@@ -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
@@ -29,9 +29,7 @@ module Avo
29
29
  end
30
30
 
31
31
  def frame_url
32
- Avo::Services::URIService.parse(@resource.record_path)
33
- .append_path(id.to_s)
34
- .append_query(turbo_frame: turbo_frame.to_s).to_s
32
+ "#{@resource.record_path}/#{id}?turbo_frame=#{turbo_frame}"
35
33
  end
36
34
 
37
35
  # The value
@@ -22,10 +22,7 @@ module Avo
22
22
  end
23
23
 
24
24
  def frame_url
25
- Avo::Services::URIService.parse(@resource.record_path)
26
- .append_paths(id, value.id)
27
- .append_query(turbo_frame: turbo_frame)
28
- .to_s
25
+ "#{@resource.record_path}/#{id}/#{value.id}?turbo_frame=#{turbo_frame}"
29
26
  end
30
27
 
31
28
  def fill_field(model, key, value, params)
@@ -5,13 +5,6 @@ class Avo::Menu::Builder
5
5
  end
6
6
  end
7
7
 
8
- delegate :context, to: Avo::App
9
- delegate :current_user, to: Avo::App
10
- delegate :params, to: Avo::App
11
- delegate :request, to: Avo::App
12
- delegate :root_path, to: Avo::App
13
- delegate :view_context, to: Avo::App
14
-
15
8
  def initialize(name: nil, items: [])
16
9
  @menu = Avo::Menu::Menu.new
17
10
 
@@ -67,7 +60,7 @@ class Avo::Menu::Builder
67
60
  # Add all the tools
68
61
  def all_tools(**args)
69
62
  Avo::App.tools_for_navigation.each do |tool|
70
- link tool.humanize, path: root_path(paths: [tool])
63
+ link tool.humanize, path: "#{root_path}/#{tool}"
71
64
  end
72
65
  end
73
66
 
@@ -75,4 +68,10 @@ class Avo::Menu::Builder
75
68
  def build
76
69
  @menu
77
70
  end
71
+
72
+ protected
73
+
74
+ def root_path
75
+ Avo::App.root_path
76
+ end
78
77
  end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "2.9.1.pre7" unless const_defined?(:VERSION)
2
+ VERSION = "2.9.2.pre1" unless const_defined?(:VERSION)
3
3
  end
data/lib/avo.rb CHANGED
@@ -5,7 +5,6 @@ require_relative "avo/engine" if defined?(Rails)
5
5
  loader = Zeitwerk::Loader.for_gem
6
6
  loader.inflector.inflect(
7
7
  "html" => "HTML",
8
- "uri_service" => "URIService",
9
8
  "has_html_attributes" => "HasHTMLAttributes"
10
9
  )
11
10
  loader.ignore("#{__dir__}/generators")