avo 2.9.1.pre5 → 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.

@@ -6,7 +6,6 @@
6
6
  'date-field-target': 'input',
7
7
  'first-day-of-week': @field.first_day_of_week,
8
8
  'picker-format': @field.picker_format,
9
- 'disable-mobile': @field.disable_mobile,
10
9
  'enable-time': false,
11
10
  format: @field.format,
12
11
  placeholder: @field.placeholder,
@@ -1,16 +1,31 @@
1
1
  <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
2
- <div data-controller="date-field">
3
- <%= @form.datetime_field @field.id,
2
+ <%= content_tag :div, data: {
3
+ controller: "date-field",
4
+ date_field_view_value: @view,
5
+ date_field_enable_time_value: true,
6
+ date_field_picker_format_value: @field.picker_format,
7
+ date_field_first_day_of_week_value: @field.first_day_of_week,
8
+ date_field_time24_hr_value: @field.time_24hr,
9
+ date_field_timezone_value: @field.timezone,
10
+ } do %>
11
+ <%= datetime_field "fake_#{@field.id}", "fake",
12
+ value: @field.edit_formatted_value,
4
13
  class: classes("w-full"),
14
+ data: {
15
+ 'date-field-target': 'fakeInput',
16
+ placeholder: @field.placeholder,
17
+ relative: @field.relative,
18
+ **@field.get_html(:data, view: view, element: :input)
19
+ },
20
+ disabled: @field.readonly,
21
+ placeholder: @field.placeholder,
22
+ style: @field.get_html(:style, view: view, element: :input)
23
+ %>
24
+ <%= @form.text_field @field.id,
25
+ value: @field.edit_formatted_value,
26
+ class: classes("w-full hidden"),
5
27
  data: {
6
28
  'date-field-target': 'input',
7
- 'first-day-of-week': @field.first_day_of_week,
8
- 'picker-format': @field.picker_format,
9
- 'disable-mobile': @field.disable_mobile,
10
- 'enable-time': true,
11
- time24hr: @field.time_24hr,
12
- timezone: @field.timezone,
13
- format: @field.format,
14
29
  placeholder: @field.placeholder,
15
30
  relative: @field.relative,
16
31
  **@field.get_html(:data, view: view, element: :input)
@@ -19,5 +34,5 @@
19
34
  placeholder: @field.placeholder,
20
35
  style: @field.get_html(:style, view: view, element: :input)
21
36
  %>
22
- </div>
37
+ <% end %>
23
38
  <% end %>
@@ -1,3 +1,11 @@
1
1
  <%= index_field_wrapper field: @field, resource: @resource do %>
2
- <%= @field.formatted_value %>
2
+ <%= content_tag :div, data: {
3
+ controller: "date-field",
4
+ date_field_view_value: @view,
5
+ date_field_format_value: @field.format,
6
+ date_field_timezone_value: @field.timezone,
7
+ date_field_picker_format_value: @field.picker_format,
8
+ } do %>
9
+ <%= @field.formatted_value %>
10
+ <% end %>
3
11
  <% end %>
@@ -1,3 +1,11 @@
1
1
  <%= show_field_wrapper field: @field, resource: @resource, index: @index do %>
2
- <%= @field.formatted_value %>
2
+ <%= content_tag :div, data: {
3
+ controller: "date-field",
4
+ date_field_view_value: @view,
5
+ date_field_format_value: @field.format,
6
+ date_field_timezone_value: @field.timezone,
7
+ date_field_picker_format_value: @field.picker_format,
8
+ } do %>
9
+ <%= @field.formatted_value %>
10
+ <% end %>
3
11
  <% end %>
@@ -32,7 +32,7 @@ class Avo::Views::ResourceEditComponent < Avo::ResourceComponent
32
32
  # The save button is dependent on the edit? policy method.
33
33
  # The update? method should be called only when the user clicks the Save button so the developer gets access to the params from the form.
34
34
  def can_see_the_save_button?
35
- @resource.authorization.authorize_action @view, raise_exception: false
35
+ @resource.authorization.authorize_action :edit, raise_exception: false
36
36
  end
37
37
 
38
38
  private
@@ -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
  }
data/lib/avo/app.rb CHANGED
@@ -31,12 +31,12 @@ module Avo
31
31
 
32
32
  def init(request:, context:, current_user:, root_path:, view_context:, params:)
33
33
  self.error_messages = []
34
+ self.request = request
34
35
  self.context = context
35
36
  self.current_user = current_user
36
- self.params = params
37
- self.request = request
38
37
  self.root_path = root_path
39
38
  self.view_context = view_context
39
+ self.params = params
40
40
 
41
41
  self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
42
42
  self.translation_enabled = license.has(:localization)
@@ -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
@@ -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
@@ -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
 
@@ -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.pre5" unless const_defined?(:VERSION)
2
+ VERSION = "2.9.2.pre1" unless const_defined?(:VERSION)
3
3
  end