avo 2.17.1.pre.2.customauthorizationclients → 2.17.1.pre.4.issue.1342

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +6 -5
  3. data/app/components/avo/button_component.rb +2 -2
  4. data/app/components/avo/fields/common/single_file_viewer_component.html.erb +3 -3
  5. data/app/components/avo/fields/date_field/edit_component.html.erb +2 -0
  6. data/app/components/avo/fields/date_field/index_component.html.erb +1 -0
  7. data/app/components/avo/fields/date_field/show_component.html.erb +1 -0
  8. data/app/components/avo/fields/date_time_field/edit_component.html.erb +3 -0
  9. data/app/components/avo/fields/date_time_field/index_component.html.erb +2 -0
  10. data/app/components/avo/fields/date_time_field/show_component.html.erb +2 -0
  11. data/app/components/avo/fields/time_field/edit_component.html.erb +40 -0
  12. data/app/components/avo/fields/time_field/edit_component.rb +4 -0
  13. data/app/components/avo/fields/time_field/index_component.html.erb +15 -0
  14. data/app/components/avo/fields/time_field/index_component.rb +4 -0
  15. data/app/components/avo/fields/time_field/show_component.html.erb +15 -0
  16. data/app/components/avo/fields/time_field/show_component.rb +4 -0
  17. data/app/components/avo/index/resource_controls_component.html.erb +2 -2
  18. data/app/components/avo/panel_component.html.erb +2 -4
  19. data/app/components/avo/panel_component.rb +2 -4
  20. data/app/components/avo/sidebar_profile_component.html.erb +4 -1
  21. data/app/components/avo/tab_switcher_component.html.erb +1 -1
  22. data/app/components/avo/tab_switcher_component.rb +2 -0
  23. data/app/components/avo/views/resource_edit_component.html.erb +10 -12
  24. data/app/components/avo/views/resource_index_component.html.erb +2 -4
  25. data/app/components/avo/views/resource_show_component.html.erb +21 -25
  26. data/app/helpers/avo/application_helper.rb +4 -0
  27. data/app/javascript/js/controllers/fields/date_field_controller.js +73 -21
  28. data/app/javascript/js/controllers/search_controller.js +3 -0
  29. data/avo.gemspec +1 -1
  30. data/db/factories.rb +1 -0
  31. data/lib/avo/concerns/handles_field_args.rb +4 -0
  32. data/lib/avo/concerns/has_fields.rb +25 -0
  33. data/lib/avo/fields/base_field.rb +5 -1
  34. data/lib/avo/fields/date_field.rb +2 -0
  35. data/lib/avo/fields/date_time_field.rb +2 -0
  36. data/lib/avo/fields/time_field.rb +49 -0
  37. data/lib/avo/version.rb +1 -1
  38. data/lib/generators/avo/templates/locales/avo.nb.yml +1 -1
  39. data/lib/generators/avo/templates/locales/avo.nn.yml +7 -7
  40. data/public/avo-assets/avo.base.css +12 -6
  41. data/public/avo-assets/avo.base.js +88 -88
  42. data/public/avo-assets/avo.base.js.map +2 -2
  43. metadata +13 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e76409a6dd8d5c3a89a467e4cbed5e46f0c292988fd60efa5a1c673be10e36a
4
- data.tar.gz: '09bde2e9be219d3f9af5fb3256ee9ed60c77d86d587e8f9131c8d3e95d03ef29'
3
+ metadata.gz: 0cb67ed800f6808830e072dfeaf5acb53a9c8960d7ca1b1fc01a5ac27ab478b4
4
+ data.tar.gz: 861965614539773f8c6242e211bb7e3449b8393ad2306cf3aa1bcbb030a36212
5
5
  SHA512:
6
- metadata.gz: 82ceba1946394ea1bea5cf4ef8283d75ddb40fdcfcfdd55ee19cad8aff34ed873698442dd846711134e6d961e288ca723cf8f9fab0aab122c0a49319e432ff05
7
- data.tar.gz: 3829a9ec1348318c537aee5954463c41864ee710790a8a333c2f075102d205ec90e50e70f766051aa2e892c2d0d8609b0bedc299a502d557a0806e9c34107269
6
+ metadata.gz: 5a2d565a8a5839e25f23cd4bdac2308c07a5b89dacadd090b8ddcaf5180b58fa4d7f844ebafbd02c167d60d9f0f5193c6b7cd2790c61f538fc8aadfa50e6f32b
7
+ data.tar.gz: e1a6e356003227ff73440bd51a8aecdce3f4522be2c4cb0829fbd35a88d9916ffcb860638428f19296d66e054396f8683f759852282c22559b6f76fd415a8d81
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (2.17.1.pre.2.customauthorizationclients)
4
+ avo (2.17.1.pre.4.issue.1342)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -17,7 +17,7 @@ PATH
17
17
  pundit
18
18
  rails (>= 6.0)
19
19
  turbo-rails
20
- view_component (= 2.60)
20
+ view_component
21
21
  zeitwerk
22
22
 
23
23
  GEM
@@ -265,7 +265,7 @@ GEM
265
265
  net-protocol
266
266
  timeout
267
267
  nio4r (2.5.8)
268
- nokogiri (1.13.8)
268
+ nokogiri (1.13.9)
269
269
  mini_portile2 (~> 2.8.0)
270
270
  racc (~> 1.4)
271
271
  orm_adapter (0.5.0)
@@ -413,8 +413,9 @@ GEM
413
413
  tzinfo (2.0.5)
414
414
  concurrent-ruby (~> 1.0)
415
415
  unicode-display_width (2.2.0)
416
- view_component (2.60.0)
416
+ view_component (2.74.1)
417
417
  activesupport (>= 5.0.0, < 8.0)
418
+ concurrent-ruby (~> 1.0)
418
419
  method_source (~> 1.0)
419
420
  warden (1.2.9)
420
421
  rack (>= 2.0.9)
@@ -437,7 +438,7 @@ GEM
437
438
  websocket-extensions (0.1.5)
438
439
  xpath (3.2.0)
439
440
  nokogiri (~> 1.8)
440
- zeitwerk (2.6.0)
441
+ zeitwerk (2.6.1)
441
442
 
442
443
  PLATFORMS
443
444
  ruby
@@ -88,8 +88,8 @@ class Avo::ButtonComponent < ViewComponent::Base
88
88
  end
89
89
 
90
90
  def output_button
91
- if @args[:method].present?
92
- button_to @args[:url], **args do
91
+ if args.dig(:method).present? || args.dig(:data, :turbo_method).present?
92
+ button_to args[:url], **args do
93
93
  full_content
94
94
  end
95
95
  else
@@ -36,15 +36,15 @@
36
36
  <% if @resource.authorization.authorize_action(:delete_attachments?, raise_exception: false) %>
37
37
  <%= a_link destroy_path,
38
38
  icon: 'heroicons/outline/trash',
39
- method: :delete,
40
39
  color: :red,
41
40
  compact: true,
42
41
  size: :xs,
43
42
  class: 'text-center',
44
43
  title: t('avo.delete_file', item: file.filename),
45
44
  data: {
46
- 'turbo-frame': 'destroy_attachment_form',
47
- confirm: t('avo.are_you_sure'),
45
+ turbo_method: :delete,
46
+ turbo_frame: 'destroy_attachment_form',
47
+ turbo_confirm: t('avo.are_you_sure'),
48
48
  tippy: :tooltip
49
49
  } %>
50
50
  <% end %>
@@ -7,6 +7,8 @@
7
7
  date_field_picker_format_value: @field.picker_format,
8
8
  date_field_first_day_of_week_value: @field.first_day_of_week,
9
9
  date_field_disable_mobile_value: @field.disable_mobile,
10
+ date_field_field_type_value: "date",
11
+ date_field_picker_options_value: @field.picker_options,
10
12
  } do %>
11
13
  <%= datetime_field "fake_#{@field.id}", "fake",
12
14
  value: @field.edit_formatted_value,
@@ -3,6 +3,7 @@
3
3
  controller: "date-field",
4
4
  date_field_view_value: @view,
5
5
  date_field_format_value: @field.format,
6
+ date_field_field_type_value: "date",
6
7
  } do %>
7
8
  <%= @field.formatted_value %>
8
9
  <% end %>
@@ -3,6 +3,7 @@
3
3
  controller: "date-field",
4
4
  date_field_view_value: @view,
5
5
  date_field_format_value: @field.format,
6
+ date_field_field_type_value: "date",
6
7
  } do %>
7
8
  <%= @field.formatted_value %>
8
9
  <% end %>
@@ -8,6 +8,9 @@
8
8
  date_field_disable_mobile_value: @field.disable_mobile,
9
9
  date_field_time24_hr_value: @field.time_24hr,
10
10
  date_field_timezone_value: @field.timezone,
11
+ date_field_relative_value: @field.relative,
12
+ date_field_field_type_value: "dateTime",
13
+ date_field_picker_options_value: @field.picker_options,
11
14
  } do %>
12
15
  <%= datetime_field "fake_#{@field.id}", "fake",
13
16
  value: @field.edit_formatted_value,
@@ -5,7 +5,9 @@
5
5
  date_field_enable_time_value: true,
6
6
  date_field_format_value: @field.format,
7
7
  date_field_timezone_value: @field.timezone,
8
+ date_field_relative_value: @field.relative,
8
9
  date_field_picker_format_value: @field.picker_format,
10
+ date_field_field_type_value: "dateTime",
9
11
  } do %>
10
12
  <%= @field.formatted_value %>
11
13
  <% end %>
@@ -5,7 +5,9 @@
5
5
  date_field_enable_time_value: true,
6
6
  date_field_format_value: @field.format,
7
7
  date_field_timezone_value: @field.timezone,
8
+ date_field_relative_value: @field.relative,
8
9
  date_field_picker_format_value: @field.picker_format,
10
+ date_field_field_type_value: "dateTime",
9
11
  } do %>
10
12
  <%= @field.formatted_value %>
11
13
  <% end %>
@@ -0,0 +1,40 @@
1
+ <%= field_wrapper **field_wrapper_args do %>
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_disable_mobile_value: @field.disable_mobile,
8
+ date_field_time24_hr_value: @field.time_24hr,
9
+ date_field_no_calendar_value: true,
10
+ date_field_timezone_value: @field.timezone,
11
+ date_field_relative_value: @field.relative,
12
+ date_field_field_type_value: "time",
13
+ date_field_picker_options_value: @field.picker_options,
14
+ } do %>
15
+ <%= datetime_field "fake_#{@field.id}", "fake",
16
+ value: @field.edit_formatted_value,
17
+ class: classes("w-full"),
18
+ data: {
19
+ 'date-field-target': 'fakeInput',
20
+ placeholder: @field.placeholder,
21
+ **@field.get_html(:data, view: view, element: :input)
22
+ },
23
+ disabled: @field.is_readonly?,
24
+ placeholder: @field.placeholder,
25
+ style: @field.get_html(:style, view: view, element: :input)
26
+ %>
27
+ <%= @form.text_field @field.id,
28
+ value: @field.edit_formatted_value,
29
+ class: classes("w-full hidden"),
30
+ data: {
31
+ 'date-field-target': 'input',
32
+ placeholder: @field.placeholder,
33
+ **@field.get_html(:data, view: view, element: :input)
34
+ },
35
+ disabled: @field.is_readonly?,
36
+ placeholder: @field.placeholder,
37
+ style: @field.get_html(:style, view: view, element: :input)
38
+ %>
39
+ <% end %>
40
+ <% end %>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Fields::TimeField::EditComponent < Avo::Fields::EditComponent
4
+ end
@@ -0,0 +1,15 @@
1
+ <%= index_field_wrapper **field_wrapper_args do %>
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_format_value: @field.format,
7
+ date_field_timezone_value: @field.timezone,
8
+ date_field_picker_format_value: @field.picker_format,
9
+ date_field_no_calendar_value: true,
10
+ date_field_relative_value: @field.relative,
11
+ date_field_field_type_value: "time",
12
+ } do %>
13
+ <%= @field.formatted_value %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Fields::TimeField::IndexComponent < Avo::Fields::IndexComponent
4
+ end
@@ -0,0 +1,15 @@
1
+ <%= field_wrapper **field_wrapper_args do %>
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_format_value: @field.format,
7
+ date_field_timezone_value: @field.timezone,
8
+ date_field_picker_format_value: @field.picker_format,
9
+ date_field_no_calendar_value: true,
10
+ date_field_relative_value: @field.relative,
11
+ date_field_field_type_value: "time",
12
+ } do %>
13
+ <%= @field.formatted_value %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Fields::TimeField::ShowComponent < Avo::Fields::ShowComponent
4
+ end
@@ -42,7 +42,7 @@
42
42
  type: :submit,
43
43
  data: {
44
44
  target: 'control:detach',
45
- confirm: t('avo.are_you_sure_detach_item', item: singular_resource_name),
45
+ turbo_confirm: t('avo.are_you_sure_detach_item', item: singular_resource_name),
46
46
  control: :detach,
47
47
  'resource-id': @resource.model.id,
48
48
  'tippy': 'tooltip',
@@ -66,7 +66,7 @@
66
66
  type: :submit,
67
67
  data: {
68
68
  target: 'control:destroy',
69
- confirm: t('avo.are_you_sure', item: singular_resource_name),
69
+ turbo_confirm: t('avo.are_you_sure', item: singular_resource_name),
70
70
  control: :destroy,
71
71
  'resource-id': @resource.model.id,
72
72
  'tippy': 'tooltip',
@@ -25,10 +25,8 @@
25
25
  <% end %>
26
26
  <% if body? %>
27
27
  <div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:gap-4 w-full">
28
- <div class="flex-1 overflow-auto <% if sidebar? %> w-2/3 <% end %>">
29
- <div class="relative <%= white_panel_classes %> <%= @body_classes %>">
30
- <%= body %>
31
- </div>
28
+ <div class="relative flex-1 <%= white_panel_classes %> <%= @body_classes %> <% if sidebar? %> w-2/3 overflow-auto <% else %> w-full <% end %>">
29
+ <%= body %>
32
30
  </div>
33
31
  <% if sidebar? %>
34
32
  <div class="w-full sm:w-1/3 flex-shrink-0 h-full <%= white_panel_classes %>">
@@ -5,6 +5,8 @@ class Avo::PanelComponent < ViewComponent::Base
5
5
  attr_reader :name
6
6
  attr_reader :classes
7
7
 
8
+ delegate :white_panel_classes, to: :helpers
9
+
8
10
  renders_one :tools
9
11
  renders_one :body
10
12
  renders_one :sidebar
@@ -26,10 +28,6 @@ class Avo::PanelComponent < ViewComponent::Base
26
28
 
27
29
  private
28
30
 
29
- def white_panel_classes
30
- "bg-white rounded shadow"
31
- end
32
-
33
31
  def data_attributes
34
32
  @data.merge({"panel-index": @index})
35
33
  end
@@ -37,8 +37,11 @@
37
37
  <%# Example link below %>
38
38
  <%#= render Avo::ProfileItemComponent.new label: 'Profile', path: '/profile', icon: 'user-circle' %>
39
39
  <%= button_to helpers.main_app.send(destroy_user_session_path),
40
- method: :delete,
41
40
  form: { "data-turbo" => "false" },
41
+ method: :delete,
42
+ data: {
43
+ confirm: t('avo.are_you_sure')
44
+ },
42
45
  class: "flex-1 flex items-center justify-center bg-white text-left cursor-pointer text-red-600 font-semibold hover:bg-red-100 block px-4 py-1 w-full py-3 text-center rounded w-full",
43
46
  form_class: 'flex-1' do %>
44
47
  <%= helpers.svg 'logout', class: 'h-4 mr-1' %> <%= t('avo.sign_out') %>
@@ -20,7 +20,7 @@
20
20
  </div>
21
21
  </div>
22
22
  <% else %>
23
- <div class="flex flex-wrap gap-2 bg-white p-2" data-target="tab-switcher" data-style="pills">
23
+ <div class="flex flex-wrap gap-2 p-2 <%= white_panel_classes %>" data-target="tab-switcher" data-style="pills">
24
24
  <% visible_items.each do |tab| %>
25
25
  <%= a_link tab_path(tab),
26
26
  color: selected?(tab) ? :primary : :gray,
@@ -11,6 +11,8 @@ class Avo::TabSwitcherComponent < Avo::BaseComponent
11
11
  attr_reader :view
12
12
  attr_reader :style
13
13
 
14
+ delegate :white_panel_classes, to: :helpers
15
+
14
16
  def initialize(resource:, group:, current_tab:, active_tab_name:, view:, style:)
15
17
  @active_tab_name = active_tab_name
16
18
  @resource = resource
@@ -25,18 +25,16 @@
25
25
  <% end %>
26
26
  <% if can_see_the_destroy_button? %>
27
27
  <%= a_link destroy_path,
28
- method: :delete,
29
- local: true,
30
- style: :text,
31
- loading: true,
32
- confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
33
- color: :red,
34
- icon: 'trash',
35
- form_class: 'flex flex-col sm:flex-row sm:inline-flex',
36
- data: {
37
- control: :destroy,
38
- 'resource-id': @resource.model.id,
39
- } do %>
28
+ style: :text,
29
+ color: :red,
30
+ icon: 'trash',
31
+ form_class: 'flex flex-col sm:flex-row sm:inline-flex',
32
+ data: {
33
+ turbo_confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
34
+ turbo_method: :delete,
35
+ control: :destroy,
36
+ 'resource-id': @resource.model.id,
37
+ } do %>
40
38
  <%= t('avo.delete').capitalize %>
41
39
  <% end %>
42
40
  <% end %>
@@ -46,10 +46,8 @@
46
46
  </div>
47
47
  <% if view_type.to_sym == :table %>
48
48
  <% if @resources.present? %>
49
- <div class="w-full overflow-auto flex flex-col mt-0 mac-styled-scrollbar">
50
- <div class="relative flex-1 flex">
51
- <%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, pagy: @pagy, query: @query)) %>
52
- </div>
49
+ <div class="w-full relative flex-1 flex mac-styled-scrollbar overflow-auto mt-0">
50
+ <%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, pagy: @pagy, query: @query)) %>
53
51
  </div>
54
52
  <% else %>
55
53
  <%= helpers.empty_state resource_name: @resource.name.downcase.pluralize, related_name: params[:related_name], view_type: view_type, add_background: true %>
@@ -21,17 +21,15 @@
21
21
  <% end %>
22
22
  <% elsif control.delete_button? %>
23
23
  <% if can_see_the_destroy_button? %>
24
- <%= a_button url: helpers.resource_path(model: @resource.model, resource: @resource),
25
- method: :delete,
26
- local: true,
24
+ <%= a_link helpers.resource_path(model: @resource.model, resource: @resource),
27
25
  style: :text,
28
- loading: true,
29
- confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
30
26
  color: :red,
31
27
  icon: 'trash',
32
28
  form_class: 'flex flex-col sm:flex-row sm:inline-flex',
33
29
  title: control.title,
34
30
  data: {
31
+ turbo_confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
32
+ turbo_method: :delete,
35
33
  control: :destroy,
36
34
  tippy: control.title ? :tooltip : nil,
37
35
  'resource-id': @resource.model.id,
@@ -84,13 +82,13 @@
84
82
  <% elsif control.detach_button? %>
85
83
  <% if @reflection.present? && @resource.model.present? && can_detach? %>
86
84
  <%= a_button url: detach_path,
87
- icon: 'detach',
88
- method: :delete,
89
- form_class: 'flex flex-col sm:flex-row sm:inline-flex',
90
- style: :text,
91
- data: {
92
- confirm: "Are you sure you want to detach this #{title}."
93
- } do %>
85
+ icon: 'detach',
86
+ method: :delete,
87
+ form_class: 'flex flex-col sm:flex-row sm:inline-flex',
88
+ style: :text,
89
+ data: {
90
+ confirm: "Are you sure you want to detach this #{title}."
91
+ } do %>
94
92
  <%= control.label %>
95
93
  <% end %>
96
94
  <% end %>
@@ -126,19 +124,17 @@
126
124
  <%= t('avo.go_back') %>
127
125
  <% end %>
128
126
  <% if can_see_the_destroy_button? %>
129
- <%= a_button url: destroy_path,
130
- method: :delete,
131
- local: true,
132
- style: :text,
133
- loading: true,
134
- confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
135
- color: :red,
136
- icon: 'trash',
137
- form_class: 'flex flex-col sm:flex-row sm:inline-flex',
138
- data: {
139
- control: :destroy,
140
- 'resource-id': @resource.model.id,
141
- } do %>
127
+ <%= a_link destroy_path,
128
+ style: :text,
129
+ color: :red,
130
+ icon: 'trash',
131
+ form_class: 'flex flex-col sm:flex-row sm:inline-flex',
132
+ data: {
133
+ turbo_confirm: t('avo.are_you_sure', item: @resource.model.model_name.name.downcase),
134
+ turbo_method: :delete,
135
+ control: :destroy,
136
+ 'resource-id': @resource.model.id,
137
+ } do %>
142
138
  <%= t('avo.delete').capitalize %>
143
139
  <% end %>
144
140
  <% end %>
@@ -89,6 +89,10 @@ module Avo
89
89
  classes
90
90
  end
91
91
 
92
+ def white_panel_classes
93
+ "bg-white rounded shadow-md"
94
+ end
95
+
92
96
  def get_model_class(model)
93
97
  if model.instance_of?(Class)
94
98
  model
@@ -7,6 +7,9 @@ function universalTimestamp(timestampStr) {
7
7
  return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000))
8
8
  }
9
9
 
10
+ const RAW_DATE_FORMAT = 'y/LL/dd'
11
+ const RAW_TIME_FORMAT = 'TT'
12
+
10
13
  export default class extends Controller {
11
14
  static targets = ['input', 'fakeInput']
12
15
 
@@ -19,6 +22,10 @@ export default class extends Controller {
19
22
  firstDayOfWeek: Number,
20
23
  time24Hr: Boolean,
21
24
  disableMobile: Boolean,
25
+ noCalendar: Boolean,
26
+ relative: Boolean,
27
+ fieldType: { type: String, default: 'dateTime' },
28
+ pickerOptions: { type: Object, default: {} },
22
29
  }
23
30
 
24
31
  flatpickrInstance;
@@ -53,6 +60,18 @@ export default class extends Controller {
53
60
  return this.viewValue === 'show'
54
61
  }
55
62
 
63
+ get fieldIsDate() {
64
+ return this.fieldTypeValue === 'date'
65
+ }
66
+
67
+ get fieldIsDateTime() {
68
+ return this.fieldTypeValue === 'dateTime'
69
+ }
70
+
71
+ get fieldIsTime() {
72
+ return this.fieldTypeValue === 'time'
73
+ }
74
+
56
75
  // Parse the time as if it were UTC
57
76
  get parsedValue() {
58
77
  return DateTime.fromISO(this.initialValue, { zone: 'UTC' })
@@ -90,8 +109,8 @@ export default class extends Controller {
90
109
  initShow() {
91
110
  let value = this.parsedValue
92
111
 
93
- // Set the zone only if the type of field is date time.
94
- if (this.enableTimeValue) {
112
+ // Set the zone only if the type of field is date time or relative time.
113
+ if (this.enableTimeValue && this.relativeValue) {
95
114
  value = value.setZone(this.displayTimezone)
96
115
  }
97
116
 
@@ -109,6 +128,8 @@ export default class extends Controller {
109
128
  },
110
129
  altInput: true,
111
130
  onChange: this.onChange.bind(this),
131
+ noCalendar: false,
132
+ ...this.pickerOptionsValue,
112
133
  }
113
134
 
114
135
  // Set the format of the displayed input field.
@@ -124,24 +145,45 @@ export default class extends Controller {
124
145
  options.enableTime = this.enableTimeValue
125
146
  options.enableSeconds = this.enableTimeValue
126
147
 
127
- // enable timezone display
128
- if (this.enableTimeValue) {
129
- options.defaultDate = this.parsedValue.setZone(this.displayTimezone).toISO()
148
+ // Hide calendar and only keep time picker.
149
+ options.noCalendar = this.noCalendarValue
130
150
 
131
- options.dateFormat = 'Y-m-d H:i:S'
132
- } else {
133
- // 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.
134
- // Ex: 2022-01-30 will render as 2022-01-29 on an American timezone
135
- options.defaultDate = universalTimestamp(this.initialValue)
151
+ if (this.initialValue) {
152
+ // Enable timezone display
153
+ if (this.enableTimeValue && this.relativeValue) {
154
+ options.defaultDate = this.parsedValue.setZone(this.displayTimezone).toISO()
155
+
156
+ options.dateFormat = 'Y-m-d H:i:S'
157
+ } else {
158
+ // 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.
159
+ // Ex: 2022-01-30 will render as 2022-01-29 on an American timezone
160
+ options.defaultDate = universalTimestamp(this.initialValue)
161
+ }
136
162
  }
137
163
 
138
164
  this.flatpickrInstance = flatpickr(this.fakeInputTarget, options)
139
165
 
140
- if (this.enableTimeValue) {
141
- this.updateRealInput(this.parsedValue.setZone(this.displayTimezone).toISO())
142
- } else {
143
- this.updateRealInput(universalTimestamp(this.initialValue))
166
+ // Don't try to parse the value if the input is empty.
167
+ if (!this.initialValue) {
168
+ return
144
169
  }
170
+
171
+ let value
172
+ switch (this.fieldTypeValue) {
173
+ case 'time':
174
+ // For time values, we should maintain the real value and format it to a time-friendly format.
175
+ value = this.parsedValue.setZone(this.displayTimezone, { keepLocalTime: true }).toFormat(RAW_TIME_FORMAT)
176
+ break
177
+ case 'date':
178
+ value = DateTime.fromJSDate(universalTimestamp(this.initialValue)).toFormat(RAW_DATE_FORMAT)
179
+ break
180
+ default:
181
+ case 'dateTime':
182
+ value = this.parsedValue.setZone(this.displayTimezone).toISO()
183
+ break
184
+ }
185
+
186
+ this.updateRealInput(value)
145
187
  }
146
188
 
147
189
  onChange(selectedDates) {
@@ -152,24 +194,34 @@ export default class extends Controller {
152
194
  return
153
195
  }
154
196
 
155
- let time
156
197
  let args = {}
157
198
 
158
- if (this.timezoneValue) {
199
+ // For values that involve time we should keep the local time.
200
+ if (this.timezoneValue || !this.relativeValue) {
159
201
  args = { keepLocalTime: true }
160
202
  } else {
161
203
  args = { keepLocalTime: false }
162
204
  }
163
205
 
164
- if (this.enableTimeValue) {
165
- time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', args)
166
- } else {
167
- time = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: true })
206
+ let value
207
+ switch (this.fieldTypeValue) {
208
+ case 'time':
209
+ // For time values, we should maintain the real value and format it to a time-friendly format.
210
+ value = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', args).toFormat(RAW_TIME_FORMAT)
211
+ break
212
+ case 'date':
213
+ value = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: true }).toFormat(RAW_DATE_FORMAT)
214
+ break
215
+ default:
216
+ case 'dateTime':
217
+ value = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', args).toISO()
218
+ break
168
219
  }
169
220
 
170
- this.updateRealInput(time)
221
+ this.updateRealInput(value)
171
222
  }
172
223
 
224
+ // Value should be a string
173
225
  updateRealInput(value) {
174
226
  this.inputTarget.value = value
175
227
  }
@@ -73,6 +73,9 @@ export default class extends Controller {
73
73
  Mousetrap.bind(['command+k', 'ctrl+k'], () => this.showSearchPanel())
74
74
  }
75
75
 
76
+ // This line fixes a bug where the search box would be duplicated on back navigation.
77
+ this.autocompleteTarget.innerHTML = ''
78
+
76
79
  autocomplete({
77
80
  container: this.autocompleteTarget,
78
81
  placeholder: this.translationKeys.placeholder,
data/avo.gemspec CHANGED
@@ -40,7 +40,7 @@ Gem::Specification.new do |spec|
40
40
  spec.add_dependency "httparty"
41
41
  spec.add_dependency "active_link_to"
42
42
  spec.add_dependency "image_processing"
43
- spec.add_dependency "view_component", "2.60"
43
+ spec.add_dependency "view_component"
44
44
  spec.add_dependency "turbo-rails"
45
45
  spec.add_dependency "addressable"
46
46
  spec.add_dependency "meta-tags"
data/db/factories.rb CHANGED
@@ -69,6 +69,7 @@ FactoryBot.define do
69
69
  skills { [Faker::Educator.subject, Faker::Educator.subject, Faker::Educator.subject, Faker::Educator.subject, Faker::Educator.subject] }
70
70
  country { Course.countries.sample }
71
71
  city { Course.cities.stringify_keys[country].sample }
72
+ starting_at { Time.now }
72
73
  end
73
74
 
74
75
  factory :course_link, class: "Course::Link" do