Code refactoring and improvements for the booking page.

This commit is contained in:
Alex Tselegidis 2022-01-17 05:57:52 +01:00
parent ea6b13f1d2
commit 5f4ec0264e

View file

@ -17,6 +17,24 @@
* Old Name: FrontendBook * Old Name: FrontendBook
*/ */
App.Pages.Booking = (function () { App.Pages.Booking = (function () {
const $cookieNoticeLink = $('.cc-link');
const $selectDate = $('#select-date');
const $selectService = $('#select-service');
const $selectProvider = $('#select-provider');
const $selectTimezone = $('#select-timezone');
const $firstName = $('#first-name');
const $lastName = $('#last-name');
const $email = $('#email');
const $phoneNumber = $('#phone-number');
const $address = $('#address');
const $city = $('#city');
const $zipCode = $('#zip-code');
const $notes = $('#notes');
const $captchaTitle = $('.captcha-title');
const $availableHours = $('#available-hours');
const $bookAppointmentSubmit = $('#book-appointment-submit');
const $deletePersonalInformation = $('#delete-personal-information');
/** /**
* Contains terms and conditions consent. * Contains terms and conditions consent.
* *
@ -60,13 +78,13 @@ App.Pages.Booking = (function () {
} }
}); });
$('.cc-link').replaceWith( $cookieNoticeLink.replaceWith(
$('<a/>', { $('<a/>', {
'data-toggle': 'modal', 'data-toggle': 'modal',
'data-target': '#cookie-notice-modal', 'data-target': '#cookie-notice-modal',
'href': '#', 'href': '#',
'class': 'cc-link', 'class': 'cc-link',
'text': $('.cc-link').text() 'text': $cookieNoticeLink.text()
}) })
); );
} }
@ -78,7 +96,7 @@ App.Pages.Booking = (function () {
const weekDayId = GeneralFunctions.getWeekDayId(GlobalVariables.firstWeekday); const weekDayId = GeneralFunctions.getWeekDayId(GlobalVariables.firstWeekday);
$('#select-date').datepicker({ $selectDate.datepicker({
dateFormat: 'dd-mm-yy', dateFormat: 'dd-mm-yy',
firstDay: weekDayId, firstDay: weekDayId,
minDate: 0, minDate: 0,
@ -130,8 +148,8 @@ App.Pages.Booking = (function () {
currentText: App.Lang.now, currentText: App.Lang.now,
closeText: App.Lang.close, closeText: App.Lang.close,
onSelect: function (dateText, instance) { onSelect: () => {
App.Http.Booking.getAvailableHours(moment($(this).datepicker('getDate')).format('YYYY-MM-DD')); App.Http.Booking.getAvailableHours(moment($selectDate.datepicker('getDate')).format('YYYY-MM-DD'));
updateConfirmFrame(); updateConfirmFrame();
}, },
@ -139,17 +157,17 @@ App.Pages.Booking = (function () {
const currentDate = new Date(year, month - 1, 1); const currentDate = new Date(year, month - 1, 1);
App.Http.Booking.getUnavailableDates( App.Http.Booking.getUnavailableDates(
$('#select-provider').val(), $selectProvider.val(),
$('#select-service').val(), $selectService.val(),
moment(currentDate).format('YYYY-MM-DD') moment(currentDate).format('YYYY-MM-DD')
); );
} }
}); });
$('#select-timezone').val(Intl.DateTimeFormat().resolvedOptions().timeZone); $selectTimezone.val(Intl.DateTimeFormat().resolvedOptions().timeZone);
// Bind the event handlers (might not be necessary every time we use this class). // Bind the event handlers (might not be necessary every time we use this class).
bindEventHandlers(); addEventListeners();
// If the manage mode is true, the appointments data should be loaded by default. // If the manage mode is true, the appointments data should be loaded by default.
if (manageMode) { if (manageMode) {
@ -159,9 +177,6 @@ App.Pages.Booking = (function () {
GlobalVariables.customerData GlobalVariables.customerData
); );
} else { } else {
const $selectProvider = $('#select-provider');
const $selectService = $('#select-service');
// Check if a specific service was selected (via URL parameter). // Check if a specific service was selected (via URL parameter).
const selectedServiceId = GeneralFunctions.getUrlParameter(location.href, 'service'); const selectedServiceId = GeneralFunctions.getUrlParameter(location.href, 'service');
@ -192,14 +207,14 @@ App.Pages.Booking = (function () {
} }
/** /**
* Bind the event handlers. * Add the page event listeners.
*/ */
function bindEventHandlers() { function addEventListeners() {
/** /**
* Event: Timezone "Changed" * Event: Timezone "Changed"
*/ */
$('#select-timezone').on('change', () => { $selectTimezone.on('change', () => {
const date = $('#select-date').datepicker('getDate'); const date = $selectDate.datepicker('getDate');
if (!date) { if (!date) {
return; return;
@ -215,11 +230,13 @@ App.Pages.Booking = (function () {
* *
* Whenever the provider changes the available appointment date - time periods must be updated. * Whenever the provider changes the available appointment date - time periods must be updated.
*/ */
$('#select-provider').on('change', (event) => { $selectProvider.on('change', (event) => {
const $target = $(event.target);
App.Http.Booking.getUnavailableDates( App.Http.Booking.getUnavailableDates(
$(event.target).val(), $target,
$('#select-service').val(), $selectService.val(),
moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD') moment($selectDate.datepicker('getDate')).format('YYYY-MM-DD')
); );
updateConfirmFrame(); updateConfirmFrame();
}); });
@ -230,10 +247,11 @@ App.Pages.Booking = (function () {
* When the user clicks on a service, its available providers should * When the user clicks on a service, its available providers should
* become visible. * become visible.
*/ */
$('#select-service').on('change', (event) => { $selectService.on('change', (event) => {
const serviceId = $('#select-service').val(); const $target = $(event.target);
const serviceId = $selectService.val();
$('#select-provider').empty(); $selectProvider.empty();
GlobalVariables.availableProviders.forEach((provider) => { GlobalVariables.availableProviders.forEach((provider) => {
// If the current provider is able to provide the selected service, add him to the list box. // If the current provider is able to provide the selected service, add him to the list box.
@ -242,23 +260,19 @@ App.Pages.Booking = (function () {
.length > 0; .length > 0;
if (canServeService) { if (canServeService) {
$('#select-provider').append( $selectProvider.append(new Option(provider.first_name + ' ' + provider.last_name, provider.id));
new Option(provider.first_name + ' ' + provider.last_name, provider.id)
);
} }
}); });
// Add the "Any Provider" entry. // Add the "Any Provider" entry.
if ($('#select-provider option').length >= 1 && GlobalVariables.displayAnyProvider === '1') { if ($selectProvider.find('option').length >= 1 && GlobalVariables.displayAnyProvider === '1') {
$('#select-provider').prepend( $selectProvider.prepend(new Option('- ' + App.Lang.any_provider + ' -', 'any-provider', true, true));
new Option('- ' + App.Lang.any_provider + ' -', 'any-provider', true, true)
);
} }
App.Http.Booking.getUnavailableDates( App.Http.Booking.getUnavailableDates(
$('#select-provider').val(), $selectProvider.val(),
$(event.target).val(), $target.val(),
moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD') moment($selectDate.datepicker('getDate')).format('YYYY-MM-DD')
); );
updateConfirmFrame(); updateConfirmFrame();
@ -273,13 +287,15 @@ App.Pages.Booking = (function () {
* Some special tasks might be performed, depending on the current wizard step. * Some special tasks might be performed, depending on the current wizard step.
*/ */
$('.button-next').on('click', (event) => { $('.button-next').on('click', (event) => {
// If we are on the first step and there is not provider selected do not continue with the next step. const $target = $(event.target);
if ($(event.target).attr('data-step_index') === '1' && !$('#select-provider').val()) {
// If we are on the first step and there is no provider selected do not continue with the next step.
if ($target.attr('data-step_index') === '1' && !$selectProvider.val()) {
return; return;
} }
// If we are on the 2nd tab then the user should have an appointment hour selected. // If we are on the 2nd tab then the user should have an appointment hour selected.
if ($(event.target).attr('data-step_index') === '2') { if ($target.attr('data-step_index') === '2') {
if (!$('.selected-hour').length) { if (!$('.selected-hour').length) {
if (!$('#select-hour-prompt').length) { if (!$('#select-hour-prompt').length) {
$('<div/>', { $('<div/>', {
@ -294,7 +310,7 @@ App.Pages.Booking = (function () {
// If we are on the 3rd tab then we will need to validate the user's input before proceeding to the next // If we are on the 3rd tab then we will need to validate the user's input before proceeding to the next
// step. // step.
if ($(event.target).attr('data-step_index') === '3') { if ($target.attr('data-step_index') === '3') {
if (!validateCustomerForm()) { if (!validateCustomerForm()) {
return; // Validation failed, do not continue. return; // Validation failed, do not continue.
} else { } else {
@ -303,9 +319,9 @@ App.Pages.Booking = (function () {
const $acceptToTermsAndConditions = $('#accept-to-terms-and-conditions'); const $acceptToTermsAndConditions = $('#accept-to-terms-and-conditions');
if ($acceptToTermsAndConditions.length && $acceptToTermsAndConditions.prop('checked') === true) { if ($acceptToTermsAndConditions.length && $acceptToTermsAndConditions.prop('checked') === true) {
const newTermsAndConditionsConsent = { const newTermsAndConditionsConsent = {
first_name: $('#first-name').val(), first_name: $firstName.val(),
last_name: $('#last-name').val(), last_name: $lastName.val(),
email: $('#email').val(), email: $email.val(),
type: 'terms-and-conditions' type: 'terms-and-conditions'
}; };
@ -320,9 +336,9 @@ App.Pages.Booking = (function () {
const $acceptToPrivacyPolicy = $('#accept-to-privacy-policy'); const $acceptToPrivacyPolicy = $('#accept-to-privacy-policy');
if ($acceptToPrivacyPolicy.length && $acceptToPrivacyPolicy.prop('checked') === true) { if ($acceptToPrivacyPolicy.length && $acceptToPrivacyPolicy.prop('checked') === true) {
const newPrivacyPolicyConsent = { const newPrivacyPolicyConsent = {
first_name: $('#first-name').val(), first_name: $firstName.val(),
last_name: $('#last-name').val(), last_name: $lastName.val(),
email: $('#email').val(), email: $email.val(),
type: 'privacy-policy' type: 'privacy-policy'
}; };
@ -335,9 +351,9 @@ App.Pages.Booking = (function () {
} }
// Display the next step tab (uses jquery animation effect). // Display the next step tab (uses jquery animation effect).
const nextTabIndex = parseInt($(event.target).attr('data-step_index')) + 1; const nextTabIndex = parseInt($target.attr('data-step_index')) + 1;
$(event.target) $target
.parents() .parents()
.eq(1) .eq(1)
.hide('fade', () => { .hide('fade', () => {
@ -369,11 +385,10 @@ App.Pages.Booking = (function () {
/** /**
* Event: Available Hour "Click" * Event: Available Hour "Click"
* *
* Triggered whenever the user clicks on an available hour * Triggered whenever the user clicks on an available hour for his appointment.
* for his appointment.
*/ */
$('#available-hours').on('click', '.available-hour', (event) => { $availableHours.on('click', '.available-hour', (event) => {
$('.selected-hour').removeClass('selected-hour'); $availableHours.find('.selected-hour').removeClass('selected-hour');
$(event.target).addClass('selected-hour'); $(event.target).addClass('selected-hour');
updateConfirmFrame(); updateConfirmFrame();
}); });
@ -389,22 +404,26 @@ App.Pages.Booking = (function () {
* @param {jQuery.Event} event * @param {jQuery.Event} event
*/ */
$('#cancel-appointment').on('click', () => { $('#cancel-appointment').on('click', () => {
const $cancelAppointmentForm = $('#cancel-appointment-form');
let $cancelReason;
const buttons = [ const buttons = [
{ {
text: App.Lang.cancel, text: App.Lang.cancel,
click: function () { click: () => {
$('#message-box').dialog('close'); $('#message-box').dialog('close');
} }
}, },
{ {
text: 'OK', text: 'OK',
click: function () { click: () => {
if ($('#cancel-reason').val() === '') { if ($cancelReason.val() === '') {
$('#cancel-reason').css('border', '2px solid #DC3545'); $cancelReason.css('border', '2px solid #DC3545');
return; return;
} }
$('#cancel-appointment-form textarea').val($('#cancel-reason').val()); $cancelAppointmentForm.find('textarea').val($cancelReason.val());
$('#cancel-appointment-form').submit(); $cancelAppointmentForm.submit();
} }
} }
]; ];
@ -415,7 +434,7 @@ App.Pages.Booking = (function () {
buttons buttons
); );
$('<textarea/>', { $cancelReason = $('<textarea/>', {
'class': 'form-control', 'class': 'form-control',
'id': 'cancel-reason', 'id': 'cancel-reason',
'rows': '3', 'rows': '3',
@ -427,7 +446,7 @@ App.Pages.Booking = (function () {
return false; return false;
}); });
$('#delete-personal-information').on('click', () => { $deletePersonalInformation.on('click', () => {
const buttons = [ const buttons = [
{ {
text: App.Lang.cancel, text: App.Lang.cancel,
@ -460,27 +479,27 @@ App.Pages.Booking = (function () {
* *
* @param {jQuery.Event} event * @param {jQuery.Event} event
*/ */
$('#book-appointment-submit').on('click', () => { $bookAppointmentSubmit.on('click', () => {
App.Http.Booking.registerAppointment(); App.Http.Booking.registerAppointment();
}); });
/** /**
* Event: Refresh captcha image. * Event: Refresh captcha image.
*/ */
$('.captcha-title button').on('click', () => { $captchaTitle.on('click', 'button', () => {
$('.captcha-image').attr('src', GlobalVariables.baseUrl + '/index.php/captcha?' + Date.now()); $('.captcha-image').attr('src', GlobalVariables.baseUrl + '/index.php/captcha?' + Date.now());
}); });
$('#select-date').on('mousedown', '.ui-datepicker-calendar td', () => { $selectDate.on('mousedown', '.ui-datepicker-calendar td', () => {
setTimeout(() => { setTimeout(() => {
App.Http.Booking.applyPreviousUnavailableDates(); // New jQuery UI version will replace the td elements. App.Http.Booking.applyPreviousUnavailableDates();
}, 300); // There is no draw event unfortunately. }, 300);
}); });
} }
/** /**
* This function validates the customer's data input. The user cannot continue * This function validates the customer's data input. The user cannot continue without passing all the validation
* without passing all the validation checks. * checks.
* *
* @return {Boolean} Returns the validation result. * @return {Boolean} Returns the validation result.
*/ */
@ -516,8 +535,8 @@ App.Pages.Booking = (function () {
} }
// Validate email address. // Validate email address.
if ($('#email').val() && !GeneralFunctions.validateEmail($('#email').val())) { if ($email.val() && !GeneralFunctions.validateEmail($email.val())) {
$('#email').parents('.form-group').addClass('is-invalid'); $email.parents('.form-group').addClass('is-invalid');
throw new Error(App.Lang.invalid_email); throw new Error(App.Lang.invalid_email);
} }
@ -533,18 +552,18 @@ App.Pages.Booking = (function () {
* customer settings and input for the appointment booking. * customer settings and input for the appointment booking.
*/ */
function updateConfirmFrame() { function updateConfirmFrame() {
if ($('.selected-hour').text() === '') { if ($availableHours.find('.selected-hour').text() === '') {
return; return;
} }
// Appointment Details // Appointment Details
let selectedDate = $('#select-date').datepicker('getDate'); let selectedDate = $selectDate.datepicker('getDate');
if (selectedDate !== null) { if (selectedDate !== null) {
selectedDate = App.Utils.Date.format(selectedDate, App.Vars.date_format, App.Vars.time_format); selectedDate = App.Utils.Date.format(selectedDate, App.Vars.date_format, App.Vars.time_format);
} }
const serviceId = $('#select-service').val(); const serviceId = $selectService.val();
let servicePrice = ''; let servicePrice = '';
let serviceCurrency = ''; let serviceCurrency = '';
@ -566,19 +585,24 @@ App.Pages.Booking = (function () {
$('<p/>', { $('<p/>', {
'html': [ 'html': [
$('<span/>', { $('<span/>', {
'text': App.Lang.service + ': ' + $('#select-service option:selected').text() 'text': App.Lang.service + ': ' + $selectService.find('option:selected').text()
}), }),
$('<br/>'), $('<br/>'),
$('<span/>', { $('<span/>', {
'text': App.Lang.provider + ': ' + $('#select-provider option:selected').text() 'text': App.Lang.provider + ': ' + $selectProvider.find('option:selected').text()
}), }),
$('<br/>'), $('<br/>'),
$('<span/>', { $('<span/>', {
'text': App.Lang.start + ': ' + selectedDate + ' ' + $('.selected-hour').text() 'text':
App.Lang.start +
': ' +
selectedDate +
' ' +
$availableHours.find('.selected-hour').text()
}), }),
$('<br/>'), $('<br/>'),
$('<span/>', { $('<span/>', {
'text': App.Lang.timezone + ': ' + $('#select-timezone option:selected').text() 'text': App.Lang.timezone + ': ' + $selectTimezone.find('option:selected').text()
}), }),
$('<br/>'), $('<br/>'),
$('<span/>', { $('<span/>', {
@ -593,13 +617,13 @@ App.Pages.Booking = (function () {
}).appendTo('#appointment-details'); }).appendTo('#appointment-details');
// Customer Details // Customer Details
const firstName = GeneralFunctions.escapeHtml($('#first-name').val()); const firstName = GeneralFunctions.escapeHtml($firstName.val());
const lastName = GeneralFunctions.escapeHtml($('#last-name').val()); const lastName = GeneralFunctions.escapeHtml($lastName.val());
const phoneNumber = GeneralFunctions.escapeHtml($('#phone-number').val()); const phoneNumber = GeneralFunctions.escapeHtml($phoneNumber.val());
const email = GeneralFunctions.escapeHtml($('#email').val()); const email = GeneralFunctions.escapeHtml($email.val());
const address = GeneralFunctions.escapeHtml($('#address').val()); const address = GeneralFunctions.escapeHtml($address.val());
const city = GeneralFunctions.escapeHtml($('#city').val()); const city = GeneralFunctions.escapeHtml($city.val());
const zipCode = GeneralFunctions.escapeHtml($('#zip-code').val()); const zipCode = GeneralFunctions.escapeHtml($zipCode.val());
$('#customer-details').empty(); $('#customer-details').empty();
@ -643,27 +667,27 @@ App.Pages.Booking = (function () {
const data = {}; const data = {};
data.customer = { data.customer = {
last_name: $('#last-name').val(), last_name: $lastName.val(),
first_name: $('#first-name').val(), first_name: $firstName.val(),
email: $('#email').val(), email: $email.val(),
phone_number: $('#phone-number').val(), phone_number: $phoneNumber.val(),
address: $('#address').val(), address: $address.val(),
city: $('#city').val(), city: $city.val(),
zip_code: $('#zip-code').val(), zip_code: $zipCode.val(),
timezone: $('#select-timezone').val() timezone: $selectTimezone.val()
}; };
data.appointment = { data.appointment = {
start_datetime: start_datetime:
moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD') + moment($selectDate.datepicker('getDate')).format('YYYY-MM-DD') +
' ' + ' ' +
moment($('.selected-hour').data('value'), 'HH:mm').format('HH:mm') + moment($('.selected-hour').data('value'), 'HH:mm').format('HH:mm') +
':00', ':00',
end_datetime: calculateEndDatetime(), end_datetime: calculateEndDatetime(),
notes: $('#notes').val(), notes: $notes.val(),
is_unavailable: false, is_unavailable: false,
id_users_provider: $('#select-provider').val(), id_users_provider: $selectProvider.val(),
id_services: $('#select-service').val() id_services: $selectService.val()
}; };
data.manage_mode = manageMode; data.manage_mode = manageMode;
@ -685,14 +709,14 @@ App.Pages.Booking = (function () {
*/ */
function calculateEndDatetime() { function calculateEndDatetime() {
// Find selected service duration. // Find selected service duration.
const serviceId = $('#select-service').val(); const serviceId = $selectService.val();
const service = App.Vars.available_services.find( const service = App.Vars.available_services.find(
(availableService) => Number(availableService.id) === Number(serviceId) (availableService) => Number(availableService.id) === Number(serviceId)
); );
// Add the duration to the start datetime. // Add the duration to the start datetime.
const selectedDate = moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD'); const selectedDate = moment($selectDate.datepicker('getDate')).format('YYYY-MM-DD');
const selectedHour = $('.selected-hour').data('value'); // HH:mm const selectedHour = $('.selected-hour').data('value'); // HH:mm
@ -722,27 +746,27 @@ App.Pages.Booking = (function () {
function applyAppointmentData(appointment, provider, customer) { function applyAppointmentData(appointment, provider, customer) {
try { try {
// Select Service & Provider // Select Service & Provider
$('#select-service').val(appointment.id_services).trigger('change'); $selectService.val(appointment.id_services).trigger('change');
$('#select-provider').val(appointment.id_users_provider); $selectProvider.val(appointment.id_users_provider);
// Set Appointment Date // Set Appointment Date
const startMoment = moment(appointment.start_datetime); const startMoment = moment(appointment.start_datetime);
$('#select-date').datepicker('setDate', startMoment.toDate()); $selectDate.datepicker('setDate', startMoment.toDate());
App.Http.Booking.getAvailableHours(startMoment.format('YYYY-MM-DD')); App.Http.Booking.getAvailableHours(startMoment.format('YYYY-MM-DD'));
// Apply Customer's Data // Apply Customer's Data
$('#last-name').val(customer.last_name); $lastName.val(customer.last_name);
$('#first-name').val(customer.first_name); $firstName.val(customer.first_name);
$('#email').val(customer.email); $email.val(customer.email);
$('#phone-number').val(customer.phone_number); $phoneNumber.val(customer.phone_number);
$('#address').val(customer.address); $address.val(customer.address);
$('#city').val(customer.city); $city.val(customer.city);
$('#zip-code').val(customer.zip_code); $zipCode.val(customer.zip_code);
if (customer.timezone) { if (customer.timezone) {
$('#select-timezone').val(customer.timezone); $selectTimezone.val(customer.timezone);
} }
const appointmentNotes = appointment.notes !== null ? appointment.notes : ''; const appointmentNotes = appointment.notes !== null ? appointment.notes : '';
$('#notes').val(appointmentNotes); $notes.val(appointmentNotes);
updateConfirmFrame(); updateConfirmFrame();