From 289d03b87caebbdf0bb7b76859a47c6444024ccd Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Sun, 31 Jul 2022 09:45:38 +0200 Subject: [PATCH] Bug 31265: Update style of time selection controls This patch updates the style of the time selection controls so that it is clearer that they are elements which the user can interact with. The times are now styled as buttons, and a check-mark icon is added when a time is selected. Also changed: Pickup date and notes fields are hidden until a library has been selected. The count of existing appointments has been removed in favor of showing the count of available appointments. A tooltip has been added: "Appointments available: X" I also changed the way the date selection event is fired. I found that jQuery's "change" event on the input itself was not reliable for some reason. I implemented instead an "onClose" event using the flatpickr instance. Note: The patch contains some indentation changes. To test, apply the patch and enable and configure curbside pickups if necessary. You should have at least two libraries with curbside hours defined. - Log in to the OPAC and go to "your curbside pickups." - Under the "Schedule a pickup" tab you should see only the option to select a library. - When you select a library the "Pickup date" field should appear. - When you select a date the time-selection buttons and the notes field should appear. - Hovering over a time should trigger the "Appointments available" tooltip. - Clicking a time should cause the "button" to turn green with a check-mark icon. - Submit a pickup request and confirm that it is saved correctly. - Return to the "Schedule a pickup" form and confirm that selecting the same library again triggers a message, "You already have a pickup scheduled for this library." - Select another library and another date. - Switch your library selection to the library you already have a request for. The date, time, and notes fields should be hidden again and the same message triggered about already having a pickup scheduled. --- .../prog/en/modules/circ/curbside_pickups.tt | 5 + .../en/modules/opac-curbside-pickups.tt | 342 +++++++++++------- 2 files changed, 209 insertions(+), 138 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/curbside_pickups.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/curbside_pickups.tt index 45ad258ca9..dd1b972442 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/curbside_pickups.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/curbside_pickups.tt @@ -10,6 +10,9 @@ [% INCLUDE 'doc-head-open.inc' %] Curbside pickups › Circulation › Koha - +[% FILTER collapse %] + +[% END %] [% INCLUDE 'doc-head-close.inc' %] [% BLOCK cssinclude %][% END %] @@ -173,8 +231,8 @@
  1. - - [% FOR p IN policies %] @@ -182,16 +240,17 @@ [% END %] +
    Required
  2. -
  3. - +
  4. + - Required +
    Required
  5. -
  6. +
  7. -
  8. +
  9. @@ -265,146 +324,153 @@ existingPickupMoments[pickup.branchcode].push(pickupMoment); }); - $("#pickup-branch option").each(function(){ - if ( $(this).val() != "" && !policies[$(this).val()].enabled ) { - $(this).prop("disabled", "disabled"); - $(this).attr("title", _("You don't have waiting holds at this library")); - } - }); - - $('#pickup-branch').on('change', function() { - let branchcode = $(this).val(); - - let existing_pickup = pickup_exists_in.indexOf(branchcode) != -1; + $(document).ready(function() { + $("#pickup-branch option").each(function(){ + if ( $(this).val() != "" && !policies[$(this).val()].enabled ) { + $(this).prop("disabled", "disabled"); + $(this).attr("title", _("You don't have waiting holds at this library")); + } + }); - $('#pickup-date').val(""); - $('#pickup-time').val(""); - $('#pickup-times').hide(); - $('#schedule-pickup-button').prop('disabled', true); + $('#pickup-branch').on('change', function() { + let branchcode = $(this).val(); - if (existing_pickup) { - $('#existing-pickup-warning').show(); - $('#pickup-date').prop("disabled", true); - } else { - $('#existing-pickup-warning').hide(); - $('#pickup-date').prop("disabled", branchcode == ""); - } - }); + let existing_pickup = pickup_exists_in.indexOf(branchcode) != -1; - $("#pickup-date").on('change', function() { + $('#pickup-date').val(""); + $('#pickup-time').val(""); + $('#pickup-times').hide(); + $('#schedule-pickup-button').prop('disabled', true); - $('#pickup-times').empty(); - $('#schedule-pickup-button').prop( 'disabled', 1 ); + if (existing_pickup) { + $('#existing-pickup-warning').show(); + $("#pickup_date_item,#pickup_notes_item").hide(); + $('#pickup-date').prop("disabled", true); + } else { + $('#existing-pickup-warning').hide(); + $("#pickup_date_item").show(); + $('#pickup-date').prop("disabled", branchcode == ""); + } + }); - var currentDate = $(this).val(); - let branchcode = $("#pickup-branch").val(); - let policy = policies[branchcode]; + const pickupDate = document.getElementById("pickup-date"); + pickupDate._flatpickr.config.onClose.push(function( selectedDates, dateStr, instance ){ + /* Here we add an onClose event to the existing flatpickr instance */ + /* It fires after the user has selected a date from the calendar popup */ + $('#pickup-times').html(""); + $('#schedule-pickup-button').prop( 'disabled', 1 ); - let selectedDate = dayjs(currentDate, get_dateformat_str(dateformat_pref).toUpperCase()); + var currentDate = dateStr; + let branchcode = $("#pickup-branch").val(); + let policy = policies[branchcode]; - let pickupSlots = []; - let available_count = 0; - let dow = selectedDate.day(); // Sunday is 0 (at least for now) - if (!policy.slots_per_day[dow]){ - $('#pickup-times').html("
    "+_("No pickup time defined for this day.")+"
    "); - return; - } + let selectedDate = dayjs(currentDate, get_dateformat_str(dateformat_pref).toUpperCase()); - policy.slots_per_day[dow].forEach(function(slot){ - let pickup_interval = policy.pickup_interval; - if (!pickup_interval) { + let pickupSlots = []; + let available_count = 0; + let dow = selectedDate.day(); // Sunday is 0 (at least for now) + if (!policy.slots_per_day[dow]){ $('#pickup-times').html("
    "+_("No pickup time defined for this day.")+"
    "); return; } - let listStartMoment = selectedDate.hour(slot.start_hour).minute(slot.start_minute); - let listEndMoment = selectedDate.hour(slot.end_hour).minute(slot.end_minute); - - let keep_going = true; - let now = dayjs(); - - // Initialize pickup slots starting at opening time - let pickupIntervalStartMoment = listStartMoment; - let pickupIntervalEndMoment = listStartMoment.add(pickup_interval, 'minutes'); - while (keep_going) { - let available = true; - let display_slot = true; - - if (pickupIntervalStartMoment.isBefore(now)) { - // Slots in the past are unavailable - available = false; - display_slot = false; + policy.slots_per_day[dow].forEach(function(slot){ + let pickup_interval = policy.pickup_interval; + if (!pickup_interval) { + $('#pickup-times').html("
    "+_("No pickup time defined for this day.")+"
    "); + return; } - if (pickupIntervalEndMoment.isAfter(listEndMoment)) { - // Slots after the end of pickup times for the day are unavailable - available = false; + let listStartMoment = selectedDate.hour(slot.start_hour).minute(slot.start_minute); + let listEndMoment = selectedDate.hour(slot.end_hour).minute(slot.end_minute); + + let keep_going = true; + let now = dayjs(); + + // Initialize pickup slots starting at opening time + let pickupIntervalStartMoment = listStartMoment; + let pickupIntervalEndMoment = listStartMoment.add(pickup_interval, 'minutes'); + while (keep_going) { + let available = true; + let display_slot = true; + + if (pickupIntervalStartMoment.isBefore(now)) { + // Slots in the past are unavailable + available = false; + display_slot = false; + } + + if (pickupIntervalEndMoment.isAfter(listEndMoment)) { + // Slots after the end of pickup times for the day are unavailable + available = false; + } + + let pickups_scheduled = 0; + + if (existingPickupMoments[branchcode]){ + existingPickupMoments[branchcode].forEach(function(pickupMoment){ + // An existing pickup time + if (pickupMoment.isSameOrAfter(pickupIntervalStartMoment) && pickupMoment.isBefore(pickupIntervalEndMoment)) { + // This calculated pickup is in use by another scheduled pickup + pickups_scheduled++; + } + }); + } + + if (pickups_scheduled >= policy.patrons_per_interval) { + available = false; + } + + if ( display_slot ) { + pickupSlots.push( + { + "available": available, + "moment": pickupIntervalStartMoment, + "pickups_scheduled": pickups_scheduled + } + ); + } + + if ( available ) { + available_count++; + } + + pickupIntervalStartMoment = pickupIntervalEndMoment; + pickupIntervalEndMoment = pickupIntervalStartMoment.add(pickup_interval, 'minutes'); + if (pickupIntervalEndMoment.isAfter(listEndMoment)) { + // This latest slot is after the end of pickup times for the day, so we can stop + keep_going = false; + } } - let pickups_scheduled = 0; - - if (existingPickupMoments[branchcode]){ - existingPickupMoments[branchcode].forEach(function(pickupMoment){ - // An existing pickup time - if (pickupMoment.isSameOrAfter(pickupIntervalStartMoment) && pickupMoment.isBefore(pickupIntervalEndMoment)) { - // This calculated pickup is in use by another scheduled pickup - pickups_scheduled++; - } - }); - } - - if (pickups_scheduled >= policy.patrons_per_interval) { - available = false; - } - - if ( display_slot ) { - pickupSlots.push( - { - "available": available, - "moment": pickupIntervalStartMoment, - "pickups_scheduled": pickups_scheduled - } - ); - } - - if ( available ) { - available_count++; - } - - pickupIntervalStartMoment = pickupIntervalEndMoment; - pickupIntervalEndMoment = pickupIntervalStartMoment.add(pickup_interval, 'minutes'); - if (pickupIntervalEndMoment.isAfter(listEndMoment)) { - // This latest slot is after the end of pickup times for the day, so we can stop - keep_going = false; - } + $('#schedule-pickup-button').prop( 'disabled', available_count <= 0 ); + }); + + for (let i = 0; i < pickupSlots.length; i++) { + let pickupSlot = pickupSlots[i]; + let optText = pickupSlot.moment.format("HH:mm"); + let optValue = pickupSlot.moment.format("YYYY-MM-DD HH:mm:ss"); + let pickups_scheduled = pickupSlot.pickups_scheduled; + let pickups_available = policy.patrons_per_interval - pickups_scheduled; + let disabled = pickupSlot.available ? "" : "disabled"; + $("#pickup-times").append(` `); } - $('#schedule-pickup-button').prop( 'disabled', available_count <= 0 ); + $("#pickup_notes_item,#pickup-times").show(); }); - for (let i = 0; i < pickupSlots.length; i++) { - let pickupSlot = pickupSlots[i]; - let optText = pickupSlot.moment.format("HH:mm"); - let optValue = pickupSlot.moment.format("YYYY-MM-DD HH:mm:ss"); - let pickups_scheduled = pickupSlot.pickups_scheduled; - let disabled = pickupSlot.available ? "" : "disabled"; - $("#pickup-times").append(` `); - } - - $('#pickup-times').show(); - }); - - - $(document).ready(function() { - $("#pickup-times").hide(); + $("#pickup_date_item,#pickup_notes_item,#pickup-times").hide(); $("#create-pickup").on('submit', function(){ if ( ! $("input[type='radio']:checked").length ) { - alert(_("Please select a date and a pickup time")) + alert(_("Please select a date and a pickup time")); return false; } return true; }); + $("#pickup-times").tooltip({ + selector: ".pickup_select" + }); }); [% END %] -- 2.20.1