diff --git a/Gemfile b/Gemfile index b4aca7f58..3d4171794 100644 --- a/Gemfile +++ b/Gemfile @@ -41,7 +41,7 @@ gem 'rack-attack' # JS datetime library, requirement of datetime picker gem 'momentjs-rails', '~> 2.17.1' # JS datetime picker -gem 'bootstrap3-datetimepicker-rails', '~> 4.15.35' +gem 'bootstrap3-datetimepicker-rails', '~> 4.17' # Select elements for Bootstrap gem 'bootstrap-select-rails', '~> 1.12.4' gem 'uglifier', '>= 1.3.0' diff --git a/Gemfile.lock b/Gemfile.lock index e369124f7..ef655a677 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -156,7 +156,7 @@ GEM autoprefixer-rails (>= 5.2.1) sassc (>= 2.0.0) bootstrap-select-rails (1.12.4) - bootstrap3-datetimepicker-rails (4.15.35) + bootstrap3-datetimepicker-rails (4.17.47) momentjs-rails (>= 2.8.1) bootstrap_form (2.7.0) builder (3.2.3) @@ -606,7 +606,7 @@ DEPENDENCIES bootsnap bootstrap-sass (~> 3.4.1) bootstrap-select-rails (~> 1.12.4) - bootstrap3-datetimepicker-rails (~> 4.15.35) + bootstrap3-datetimepicker-rails (~> 4.17) bootstrap_form (~> 2.7.0) bullet byebug diff --git a/app/assets/javascripts/my_modules.js b/app/assets/javascripts/my_modules.js index b9ab0a806..2616c2480 100644 --- a/app/assets/javascripts/my_modules.js +++ b/app/assets/javascripts/my_modules.js @@ -1,28 +1,24 @@ /* global I18n dropdownSelector */ /* eslint-disable no-use-before-define */ +function updateDueDate() { + let updateUrl = $('.due-date-container').data('update-url'); + let val = $('#calendar-due-date').val(); + $.ajax({ + url: updateUrl, + type: 'PATCH', + dataType: 'json', + data: { my_module: { due_date: val } }, + success: function(result) { + $('#due-date-label-container').html(result.due_date_label); + } + }); +} + // Bind ajax for editing due dates function initDueDatePicker() { - function updateDueDate(val) { - var updateUrl = $('.due-date-container').data('update-url'); - $.ajax({ - url: updateUrl, - type: 'PATCH', - dataType: 'json', - data: { my_module: { due_date: val } }, - success: function(result) { - $('.due-date-container').html($(result.module_header_due_date_label)); - initDueDatePicker(); - } - }); - } - $('#calendar-due-date').on('dp.change', function() { - updateDueDate($('#calendar-due-date').val()); - }); - - $('.flex-block.date-block .clear-date').off('click').on('click', function() { - updateDueDate(null); + updateDueDate(); }); } @@ -194,7 +190,7 @@ function applyTaskCompletedCallBack() { button.find('.btn') .removeClass('btn-default').addClass('btn-primary'); } - $('.due-date-container').html(data.module_header_due_date_label); + $('.due-date-container').html(data.module_header_due_date); initDueDatePicker(); $('.task-state-label').html(data.module_state_label); button.find('button').replaceWith(data.new_btn); diff --git a/app/assets/javascripts/projects/canvas.js.erb b/app/assets/javascripts/projects/canvas.js.erb index 902d4b7a3..a9ed88fda 100644 --- a/app/assets/javascripts/projects/canvas.js.erb +++ b/app/assets/javascripts/projects/canvas.js.erb @@ -750,7 +750,7 @@ function bindEditDueDateAjax() { editDueDateModalBody.find("form") .on("ajax:success", function(ev2, data2, status2) { // Update module's due date - dueDateLink.html(data2.due_date_label); + dueDateLink.html(data2.card_due_date_label); // Update module's classes if needed moduleEl diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index 119f84164..13f92463b 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -41,7 +41,7 @@ task_button.attr('data-action', 'uncomplete-task'); task_button.find('.btn') .removeClass('btn-toggle').addClass('btn-default'); - $('.task-due-date').html(data.module_header_due_date_label); + $('.task-due-date').html(data.module_header_due_date); $('.task-state-label').html(data.module_state_label); task_button .find('button') diff --git a/app/assets/javascripts/sitewide/date_time_picker.js b/app/assets/javascripts/sitewide/date_time_picker.js new file mode 100644 index 000000000..ce64dddb3 --- /dev/null +++ b/app/assets/javascripts/sitewide/date_time_picker.js @@ -0,0 +1,24 @@ +(function() { + 'use strict'; + + $(document).on('click', '[data-toggle="date-time-picker"]', function(ev) { + ev.preventDefault(); + ev.stopPropagation(); + + let dt = $(this); + + if (dt.data('DateTimePicker')) { + dt.data('DateTimePicker').destroy(); + } + + dt.datetimepicker({ ignoreReadonly: true }); + dt.data('DateTimePicker').show(); + }); + + $(document).on('click', '[data-toggle="clear-date-time-picker"]', function() { + let dt = $(`#${$(this).data('target')}`); + if (!dt.data('DateTimePicker')) dt.datetimepicker({ useCurrent: false }); + dt.data('DateTimePicker').clear(); + dt.val(''); + }); +}()); diff --git a/app/assets/stylesheets/my_modules/protocols/index.scss b/app/assets/stylesheets/my_modules/protocols/index.scss index d5a76bf33..ad8fd4e25 100644 --- a/app/assets/stylesheets/my_modules/protocols/index.scss +++ b/app/assets/stylesheets/my_modules/protocols/index.scss @@ -63,15 +63,15 @@ padding: 0 3px; position: relative; - &.alert-green { + .alert-green { color: $brand-success; } - &.alert-yellow { + .alert-yellow { color: $brand-warning; } - &.alert-red { + .alert-red { color: $brand-danger; } diff --git a/app/controllers/my_modules_controller.rb b/app/controllers/my_modules_controller.rb index 9398f53b2..70adfbabe 100644 --- a/app/controllers/my_modules_controller.rb +++ b/app/controllers/my_modules_controller.rb @@ -145,10 +145,8 @@ class MyModulesController < ApplicationController def update update_params = my_module_params if update_params[:due_date].present? - update_params[:due_date] = Time.strptime( - update_params[:due_date].delete('-'), - I18n.backend.date_format.dup.delete('-') - ) + update_params[:due_date] = + Time.zone.strptime(update_params[:due_date], I18n.backend.date_format.dup.gsub(/%-/, '%') + ' %H:%M') end @my_module.assign_attributes(update_params) @my_module.last_modified_by = current_user @@ -211,15 +209,19 @@ class MyModulesController < ApplicationController render json: { status: :ok, due_date_label: render_to_string( - partial: "my_modules/due_date_label.html.erb", + partial: 'my_modules/due_date_label.html.erb', locals: { my_module: @my_module } ), - module_header_due_date_label: render_to_string( - partial: "my_modules/module_header_due_date_label.html.erb", + card_due_date_label: render_to_string( + partial: 'my_modules/card_due_date_label.html.erb', + locals: { my_module: @my_module } + ), + module_header_due_date: render_to_string( + partial: 'my_modules/module_header_due_date.html.erb', locals: { my_module: @my_module } ), description_label: render_to_string( - partial: "my_modules/description_label.html.erb", + partial: 'my_modules/description_label.html.erb', locals: { my_module: @my_module } ), alerts: alerts @@ -577,8 +579,8 @@ class MyModulesController < ApplicationController render json: { new_btn: render_to_string(partial: new_btn_partial), completed: completed, - module_header_due_date_label: render_to_string( - partial: 'my_modules/module_header_due_date_label.html.erb', + module_header_due_date: render_to_string( + partial: 'my_modules/module_header_due_date.html.erb', locals: { my_module: @my_module } ), module_state_label: render_to_string( @@ -605,8 +607,8 @@ class MyModulesController < ApplicationController format.json do render json: { task_button_title: t('my_modules.buttons.uncomplete'), - module_header_due_date_label: render_to_string( - partial: 'my_modules/module_header_due_date_label.html.erb', + module_header_due_date: render_to_string( + partial: 'my_modules/module_header_due_date.html.erb', locals: { my_module: @my_module } ), module_state_label: render_to_string( diff --git a/app/helpers/bootstrap_form_helper.rb b/app/helpers/bootstrap_form_helper.rb index c7479f58f..c09ed7ac3 100644 --- a/app/helpers/bootstrap_form_helper.rb +++ b/app/helpers/bootstrap_form_helper.rb @@ -22,9 +22,10 @@ module BootstrapFormHelper def datetime_picker(name, options = {}) id = "#{@object_name}_#{name.to_s}" input_name = "#{@object_name}[#{name.to_s}]" - timestamp = @object[name] ? "#{@object[name].to_i}000" : "" + date_format = I18n.backend.date_format.dup + value = options[:value] ? options[:value].strftime(date_format + ' %H:%M') : '' js_locale = I18n.locale.to_s - js_format = I18n.backend.date_format.dup + js_format = date_format js_format.gsub!(/%-d/, 'D') js_format.gsub!(/%d/, 'DD') js_format.gsub!(/%-m/, 'M') @@ -32,36 +33,27 @@ module BootstrapFormHelper js_format.gsub!(/%b/, 'MMM') js_format.gsub!(/%B/, 'MMMM') js_format.gsub!('%Y', 'YYYY') + js_format << ' HH:mm' if options[:time] == true - label = name.to_s.humanize - if options[:label] then - label = options[:label] + label = options[:label] || name.to_s.humanize + + style = options[:style] ? "style='#{options[:style]}'" : '' + + res = "
" \ + "
" + + res << "
" if options[:clear] + + res << "" + + if options[:clear] + res << "" \ + "
" end - styleStr = "" - if options[:style] then - styleStr = "style='#{options[:style]}'" - end - - jsOpts = "" - if options[:today] then - jsOpts << "showTodayButton: true, " - end - - res = "" - res << "
" - if options[:clear] then - res << "
" - end - res << "" - if options[:clear] then - res << "
" - end - res << "
" + res << '
' res.html_safe end diff --git a/app/models/my_module.rb b/app/models/my_module.rb index c9c84c031..a4d01c6f5 100644 --- a/app/models/my_module.rb +++ b/app/models/my_module.rb @@ -271,14 +271,14 @@ class MyModule < ApplicationRecord end def is_overdue?(datetime = DateTime.current) - due_date.present? && datetime.utc > due_date.end_of_day.utc + due_date.present? && datetime.utc > due_date.utc end def overdue_for_days(datetime = DateTime.current) - if due_date.blank? || due_date.end_of_day.utc > datetime.utc + if due_date.blank? || due_date.utc > datetime.utc 0 else - ((datetime.utc.to_i - due_date.end_of_day.utc.to_i) / 1.day.to_f).ceil + ((datetime.utc.to_i - due_date.utc.to_i) / 1.day.to_f).ceil end end @@ -288,8 +288,8 @@ class MyModule < ApplicationRecord def is_due_in?(datetime, diff) due_date.present? && - datetime.utc < due_date.end_of_day.utc && - datetime.utc > (due_date.end_of_day.utc - diff) + datetime.utc < due_date.utc && + datetime.utc > (due_date.utc - diff) end def space_taken diff --git a/app/views/canvas/full_zoom/_my_module.html.erb b/app/views/canvas/full_zoom/_my_module.html.erb index 5fca43646..1ff3cfe9f 100644 --- a/app/views/canvas/full_zoom/_my_module.html.erb +++ b/app/views/canvas/full_zoom/_my_module.html.erb @@ -29,17 +29,17 @@
- <% if can_manage_module?(my_module) %> + <% if !my_module.completed? && can_manage_module?(my_module) %> <%= link_to due_date_my_module_path(my_module, format: :json), remote: true, class: "due-date-link due-date-refresh help_tooltips", data: { tooltiplink: I18n.t('tooltips.link.task.due_date'), tooltipcontent: I18n.t('tooltips.text.task.due_date') } do %> - <%= render partial: "my_modules/due_date_label.html.erb", locals: { my_module: my_module } %> + <%= render partial: "my_modules/card_due_date_label.html.erb", locals: { my_module: my_module, format: :full_date } %> <% end %> <% else %> - <%= render partial: "my_modules/due_date_label.html.erb", locals: { my_module: my_module } %> + <%= render partial: "my_modules/card_due_date_label.html.erb", locals: { my_module: my_module, format: :full_date } %> <% end %>
diff --git a/app/views/my_modules/_card_due_date_label.html.erb b/app/views/my_modules/_card_due_date_label.html.erb new file mode 100644 index 000000000..4f3542788 --- /dev/null +++ b/app/views/my_modules/_card_due_date_label.html.erb @@ -0,0 +1,29 @@ +<% if my_module.completed? %> + <%= t("my_modules.states.completed") %> + + <%= l(my_module.completed_on.utc, format: :full_date) %> + + +<% elsif my_module.is_one_day_prior? %> + <%= t("my_modules.states.due_soon") %> + + <%=l my_module.due_date.utc, format: :full_date %> + + +<% elsif my_module.is_overdue? %> + <%= t("my_modules.states.overdue") %> + + <%=l my_module.due_date.utc, format: :full_date %> + + +<% elsif my_module.due_date %> + <%= t("experiments.canvas.full_zoom.due_date") %> + + <%=l my_module.due_date.utc, format: :full_date %> + +<% else %> + <%= t("experiments.canvas.full_zoom.due_date") %> + + <%=t "experiments.canvas.full_zoom.no_due_date" %> + +<% end %> diff --git a/app/views/my_modules/_due_date.html.erb b/app/views/my_modules/_due_date.html.erb index 50db90b39..ddc78b7e8 100644 --- a/app/views/my_modules/_due_date.html.erb +++ b/app/views/my_modules/_due_date.html.erb @@ -1,3 +1,7 @@ <%= bootstrap_form_for @my_module, url: my_module_path(@my_module, format: :json), remote: :true do |f| %> - <%= f.datetime_picker :due_date, label: t("my_modules.due_date.label"), clear: true %> + <%= f.datetime_picker :due_date, + value: @my_module.due_date, + label: t('my_modules.due_date.label'), + clear: true, + time: true %> <% end %> diff --git a/app/views/my_modules/_due_date_label.html.erb b/app/views/my_modules/_due_date_label.html.erb index 9d1328b4c..01bd84101 100644 --- a/app/views/my_modules/_due_date_label.html.erb +++ b/app/views/my_modules/_due_date_label.html.erb @@ -1,29 +1,19 @@ -<% if my_module.completed? %> - <%= t("my_modules.states.completed") %> - - <%= l(my_module.completed_on.utc, format: :full_date) %> - - -<% elsif my_module.is_one_day_prior? %> - <%= t("my_modules.states.due_soon") %> - - <%=l my_module.due_date.utc, format: :full_date %> - - -<% elsif my_module.is_overdue? %> - <%= t("my_modules.states.overdue") %> - - <%=l my_module.due_date.utc, format: :full_date %> - - -<% elsif my_module.due_date %> - <%= t("experiments.canvas.full_zoom.due_date") %> - - <%=l my_module.due_date.utc, format: :full_date %> - -<% else %> - <%= t("experiments.canvas.full_zoom.due_date") %> - - <%=t "experiments.canvas.full_zoom.no_due_date" %> - -<% end %> \ No newline at end of file + + <% if my_module.completed? %> + <%= t('my_modules.states.completed') %> + <%= l(my_module.completed_on, format: :full) %> + + <% elsif my_module.is_one_day_prior? %> + <%= t('my_modules.states.due_soon') %> + <%= l(my_module.due_date, format: :full) %> + + <% elsif my_module.is_overdue? %> + <%= t('my_modules.states.overdue') %> + <%= l(my_module.due_date, format: :full) %> + + <% elsif my_module.due_date %> + <%= l(my_module.due_date, format: :full) %> + <% else %> + <%= t('experiments.canvas.full_zoom.no_due_date') %> + <% end %> + diff --git a/app/views/my_modules/_module_header.html.erb b/app/views/my_modules/_module_header.html.erb index dd2f0b411..29ce458c9 100644 --- a/app/views/my_modules/_module_header.html.erb +++ b/app/views/my_modules/_module_header.html.erb @@ -6,12 +6,12 @@ <%= l(@my_module.created_at, format: :full) %> - +
- <%= render partial: "module_header_due_date_label.html.erb", + <%= render partial: "module_header_due_date.html.erb", locals: { my_module: @my_module } %>
diff --git a/app/views/my_modules/_module_header_due_date.html.erb b/app/views/my_modules/_module_header_due_date.html.erb new file mode 100644 index 000000000..39c5c5199 --- /dev/null +++ b/app/views/my_modules/_module_header_due_date.html.erb @@ -0,0 +1,33 @@ + + + <%= render partial: "due_date_label.html.erb" , locals: { my_module: my_module } %> + + <% if can_manage_module?(my_module) %> +
+ <% + js_format = I18n.backend.date_format.dup + js_format.gsub!(/%-d/, 'D') + js_format.gsub!(/%d/, 'DD') + js_format.gsub!(/%-m/, 'M') + js_format.gsub!(/%m/, 'MM') + js_format.gsub!(/%b/, 'MMM') + js_format.gsub!(/%B/, 'MMMM') + js_format.gsub!('%Y', 'YYYY') + js_format << ' HH:mm' + %> + + + +
+ + <% end %> +
diff --git a/app/views/my_modules/_module_header_due_date_label.html.erb b/app/views/my_modules/_module_header_due_date_label.html.erb deleted file mode 100644 index 528eb6836..000000000 --- a/app/views/my_modules/_module_header_due_date_label.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -<% -if my_module.completed? - date_class = "alert-green" -elsif my_module.is_one_day_prior? - date_class = "alert-yellow" - label = "(#{t('my_modules.states.due_soon')})" -elsif my_module.is_overdue? - date_class = "alert-red" - label = "(#{t('my_modules.states.overdue')})" -end -%> - - - <%= my_module.due_date ? l(my_module.due_date.utc, format: :full) : t("experiments.canvas.full_zoom.no_due_date") %> - - <% if can_manage_module?(my_module) %> - <%= render partial: "global_activities/date_picker.html.erb", locals: { - id: 'due-date', - use_current: 'false', - setDate: my_module.due_date ? l(my_module.due_date.utc, format: :full) : '', - placeholder: t("experiments.canvas.full_zoom.no_due_date"), - label: nil - } %> - <% end %> - <%= label %> - <% if my_module.due_date && can_manage_module?(my_module) %> - - <% end %> - diff --git a/spec/controllers/my_modules_controller_spec.rb b/spec/controllers/my_modules_controller_spec.rb index c41b8f3c0..cc89fbbbd 100644 --- a/spec/controllers/my_modules_controller_spec.rb +++ b/spec/controllers/my_modules_controller_spec.rb @@ -63,7 +63,7 @@ describe MyModulesController, type: :controller do context 'when setting due_date' do let(:params) do - { id: my_module.id, my_module: { due_date: '03/21/2019' } } + { id: my_module.id, my_module: { due_date: '03/21/2019 23:59' } } end it 'calls create activity for setting due date' do @@ -102,7 +102,7 @@ describe MyModulesController, type: :controller do context 'when updating due_date' do let(:params) do - { id: my_module.id, my_module: { due_date: '02/21/2019' } } + { id: my_module.id, my_module: { due_date: '02/21/2019 23:59' } } end let(:my_module) do create :my_module, :with_due_date, experiment: experiment