Refactor the booking related JS files so that they become standalone modules.

This commit is contained in:
Alex Tselegidis 2022-01-13 11:33:40 +01:00
parent 795eccb165
commit 43ad017d7a
5 changed files with 228 additions and 225 deletions

View file

@ -176,6 +176,7 @@ class Booking extends EA_Controller {
} }
script_vars([ script_vars([
'manage_mode' => $manage_mode,
'available_services' => $available_services, 'available_services' => $available_services,
'available_providers' => $available_providers, 'available_providers' => $available_providers,
'date_format' => $date_format, 'date_format' => $date_format,
@ -354,7 +355,7 @@ class Booking extends EA_Controller {
/** /**
* Register the appointment to the database. * Register the appointment to the database.
*/ */
public function ajax_register_appointment() public function register()
{ {
try try
{ {

View file

@ -107,7 +107,6 @@
<script> <script>
$(function () { $(function () {
FrontendBook.initialize(true, GlobalVariables.manageMode);
GeneralFunctions.enableLanguageSelection($('#select-language')); GeneralFunctions.enableLanguageSelection($('#select-language'));
}); });
</script> </script>

View file

@ -49,7 +49,11 @@
<?php section('scripts') ?> <?php section('scripts') ?>
<script src="<?= asset_url('assets/js/pages/frontend_book_api.js') ?>"></script> <script src="<?= asset_url('assets/js/utils/date.js') ?>"></script>
<script src="<?= asset_url('assets/js/pages/frontend_book.js') ?>"></script> <script src="<?= asset_url('assets/js/utils/message.js') ?>"></script>
<script src="<?= asset_url('assets/js/utils/validation.js') ?>"></script>
<script src="<?= asset_url('assets/js/utils/url.js') ?>"></script>
<script src="<?= asset_url('assets/js/http/booking_http_client.js') ?>"></script>
<script src="<?= asset_url('assets/js/pages/booking.js') ?>"></script>
<?php section('scripts') ?> <?php section('scripts') ?>

View file

@ -9,21 +9,17 @@
* @since v1.0.0 * @since v1.0.0
* ---------------------------------------------------------------------------- */ * ---------------------------------------------------------------------------- */
window.FrontendBookApi = window.FrontendBookApi || {};
/** /**
* Frontend Book API * Booking HTTP Client
* *
* This module serves as the API consumer for the booking wizard of the app. * This module serves as the API consumer for the booking wizard of the app.
* *
* @module FrontendBookApi * Old Name: FrontendBookApi
*/ */
(function (exports) { App.Http.Booking = (function () {
'use strict'; let unavailableDatesBackup;
let selectedDateStringBackup;
var unavailableDatesBackup; let processingUnavailabilities = false;
var selectedDateStringBackup;
var processingUnavailabilities = false;
/** /**
* Get Available Hours * Get Available Hours
@ -33,47 +29,47 @@ window.FrontendBookApi = window.FrontendBookApi || {};
* *
* @param {String} selectedDate The selected date of the available hours we need. * @param {String} selectedDate The selected date of the available hours we need.
*/ */
exports.getAvailableHours = function (selectedDate) { function getAvailableHours(selectedDate) {
$('#available-hours').empty(); $('#available-hours').empty();
// Find the selected service duration (it is going to be send within the "data" object). // Find the selected service duration (it is going to be send within the "data" object).
var serviceId = $('#select-service').val(); const serviceId = $('#select-service').val();
// Default value of duration (in minutes). // Default value of duration (in minutes).
var serviceDuration = 15; let serviceDuration = 15;
var service = GlobalVariables.availableServices.find(function (availableService) { const service = App.Vars.available_services.find(
return Number(availableService.id) === Number(serviceId); (availableService) => Number(availableService.id) === Number(serviceId)
}); );
if (service) { if (service) {
serviceDuration = service.duration; serviceDuration = service.duration;
} }
// If the manage mode is true then the appointment's start date should return as available too. // If the manage mode is true then the appointment's start date should return as available too.
var appointmentId = FrontendBook.manageMode ? GlobalVariables.appointmentData.id : null; const appointmentId = App.Pages.Booking.manageMode ? App.Vars.appointment_data.id : null;
// Make ajax post request and get the available hours. // Make ajax post request and get the available hours.
var url = GlobalVariables.baseUrl + '/index.php/booking/ajax_get_available_hours'; const url = App.Utils.Url.siteUrl('booking/ajax_get_available_hours');
var data = { const data = {
csrf_token: GlobalVariables.csrfToken, csrf_token: App.Vars.csrf_token,
service_id: $('#select-service').val(), service_id: $('#select-service').val(),
provider_id: $('#select-provider').val(), provider_id: $('#select-provider').val(),
selected_date: selectedDate, selected_date: selectedDate,
service_duration: serviceDuration, service_duration: serviceDuration,
manage_mode: FrontendBook.manageMode, manage_mode: App.Pages.Booking.manageMode,
appointment_id: appointmentId appointment_id: appointmentId
}; };
$.post(url, data).done(function (response) { $.post(url, data).done((response) => {
// The response contains the available hours for the selected provider and service. Fill the available // The response contains the available hours for the selected provider and service. Fill the available
// hours div with response data. // hours div with response data.
if (response.length > 0) { if (response.length > 0) {
var providerId = $('#select-provider').val(); let providerId = $('#select-provider').val();
if (providerId === 'any-provider') { if (providerId === 'any-provider') {
for (var availableProvider of GlobalVariables.availableProviders) { for (const availableProvider of App.Vars.available_providers) {
if (availableProvider.services.indexOf(Number(serviceId)) !== -1) { if (availableProvider.services.indexOf(Number(serviceId)) !== -1) {
providerId = availableProvider.id; // Use first available provider. providerId = availableProvider.id; // Use first available provider.
break; break;
@ -81,20 +77,20 @@ window.FrontendBookApi = window.FrontendBookApi || {};
} }
} }
var provider = GlobalVariables.availableProviders.find(function (availableProvider) { const provider = App.Vars.available_providers.find(
return Number(providerId) === Number(availableProvider.id); (availableProvider) => Number(providerId) === Number(availableProvider.id)
}); );
if (!provider) { if (!provider) {
throw new Error('Could not find provider.'); throw new Error('Could not find provider.');
} }
var providerTimezone = provider.timezone; const providerTimezone = provider.timezone;
var selectedTimezone = $('#select-timezone').val(); const selectedTimezone = $('#select-timezone').val();
var timeFormat = GlobalVariables.timeFormat === 'regular' ? 'h:mm a' : 'HH:mm'; const timeFormat = App.Vars.time_format === 'regular' ? 'h:mm a' : 'HH:mm';
response.forEach(function (availableHour) { response.forEach((availableHour) => {
var availableHourMoment = moment const availableHourMoment = moment
.tz(selectedDate + ' ' + availableHour + ':00', providerTimezone) .tz(selectedDate + ' ' + availableHour + ':00', providerTimezone)
.tz(selectedTimezone); .tz(selectedTimezone);
@ -113,30 +109,29 @@ window.FrontendBookApi = window.FrontendBookApi || {};
); );
}); });
if (FrontendBook.manageMode) { if (App.Pages.Booking.manageMode) {
// Set the appointment's start time as the default selection. // Set the appointment's start time as the default selection.
$('.available-hour') $('.available-hour')
.removeClass('selected-hour') .removeClass('selected-hour')
.filter(function () { .filter(
return ( (index, availableHourEl) =>
$(this).text() === $(availableHourEl).text() ===
moment(GlobalVariables.appointmentData.start_datetime).format(timeFormat) moment(App.Vars.appointment_data.start_datetime).format(timeFormat)
); )
})
.addClass('selected-hour'); .addClass('selected-hour');
} else { } else {
// Set the first available hour as the default selection. // Set the first available hour as the default selection.
$('.available-hour:eq(0)').addClass('selected-hour'); $('.available-hour:eq(0)').addClass('selected-hour');
} }
FrontendBook.updateConfirmFrame(); App.Pages.Booking.updateConfirmFrame();
} }
if (!$('.available-hour').length) { if (!$('.available-hour').length) {
$('#available-hours').text(App.Lang.no_available_hours); $('#available-hours').text(App.Lang.no_available_hours);
} }
}); });
}; }
/** /**
* Register an appointment to the database. * Register an appointment to the database.
@ -144,8 +139,8 @@ window.FrontendBookApi = window.FrontendBookApi || {};
* This method will make an ajax call to the appointments controller that will register * This method will make an ajax call to the appointments controller that will register
* the appointment to the database. * the appointment to the database.
*/ */
exports.registerAppointment = function () { function registerAppointment() {
var $captchaText = $('.captcha-text'); const $captchaText = $('.captcha-text');
if ($captchaText.length > 0) { if ($captchaText.length > 0) {
$captchaText.removeClass('is-invalid'); $captchaText.removeClass('is-invalid');
@ -155,10 +150,10 @@ window.FrontendBookApi = window.FrontendBookApi || {};
} }
} }
var formData = JSON.parse($('input[name="post_data"]').val()); const formData = JSON.parse($('input[name="post_data"]').val());
var data = { const data = {
csrf_token: GlobalVariables.csrfToken, csrf_token: App.Vars.csrf_token,
post_data: formData post_data: formData
}; };
@ -166,20 +161,20 @@ window.FrontendBookApi = window.FrontendBookApi || {};
data.captcha = $captchaText.val(); data.captcha = $captchaText.val();
} }
if (GlobalVariables.manageMode) { if (App.Vars.manage_mode) {
data.exclude_appointment_id = GlobalVariables.appointmentData.id; data.exclude_appointment_id = App.Vars.appointment_data.id;
} }
var url = GlobalVariables.baseUrl + '/index.php/booking/ajax_register_appointment'; const url = App.Utils.Url.siteUrl('booking/register');
var $layer = $('<div/>'); const $layer = $('<div/>');
$.ajax({ $.ajax({
url: url, url: url,
method: 'post', method: 'post',
data: data, data: data,
dataType: 'json', dataType: 'json',
beforeSend: function (jqxhr, settings) { beforeSend: () => {
$layer.appendTo('body').css({ $layer.appendTo('body').css({
background: 'white', background: 'white',
position: 'fixed', position: 'fixed',
@ -191,11 +186,11 @@ window.FrontendBookApi = window.FrontendBookApi || {};
}); });
} }
}) })
.done(function (response) { .done((response) => {
if (response.captcha_verification === false) { if (response.captcha_verification === false) {
$('#captcha-hint').text(App.Lang.captcha_is_wrong).fadeTo(400, 1); $('#captcha-hint').text(App.Lang.captcha_is_wrong).fadeTo(400, 1);
setTimeout(function () { setTimeout(() => {
$('#captcha-hint').fadeTo(400, 0); $('#captcha-hint').fadeTo(400, 0);
}, 3000); }, 3000);
@ -206,16 +201,15 @@ window.FrontendBookApi = window.FrontendBookApi || {};
return false; return false;
} }
window.location.href = window.location.href = App.Utils.Url.siteUrl('booking_confirmation/of/' + response.appointment_hash);
GlobalVariables.baseUrl + '/index.php/booking_confirmation/of/' + response.appointment_hash;
}) })
.fail(function (jqxhr, textStatus, errorThrown) { .fail(() => {
$('.captcha-title button').trigger('click'); $('.captcha-title button').trigger('click');
}) })
.always(function () { .always(() => {
$layer.remove(); $layer.remove();
}); });
}; }
/** /**
* Get the unavailable dates of a provider. * Get the unavailable dates of a provider.
@ -228,7 +222,7 @@ window.FrontendBookApi = window.FrontendBookApi || {};
* @param {Number} serviceId The selected service ID. * @param {Number} serviceId The selected service ID.
* @param {String} selectedDateString Y-m-d value of the selected date. * @param {String} selectedDateString Y-m-d value of the selected date.
*/ */
exports.getUnavailableDates = function (providerId, serviceId, selectedDateString) { function getUnavailableDates(providerId, serviceId, selectedDateString) {
if (processingUnavailabilities) { if (processingUnavailabilities) {
return; return;
} }
@ -237,16 +231,16 @@ window.FrontendBookApi = window.FrontendBookApi || {};
return; return;
} }
var appointmentId = FrontendBook.manageMode ? GlobalVariables.appointmentData.id : null; const appointmentId = App.Pages.Booking.manageMode ? App.Vars.appointment_data.id : null;
var url = GlobalVariables.baseUrl + '/index.php/booking/ajax_get_unavailable_dates'; const url = App.Utils.Url.siteUrl('booking/ajax_get_unavailable_dates');
var data = { const data = {
provider_id: providerId, provider_id: providerId,
service_id: serviceId, service_id: serviceId,
selected_date: encodeURIComponent(selectedDateString), selected_date: encodeURIComponent(selectedDateString),
csrf_token: GlobalVariables.csrfToken, csrf_token: App.Vars.csrf_token,
manage_mode: FrontendBook.manageMode, manage_mode: App.Pages.Booking.manageMode,
appointment_id: appointmentId appointment_id: appointmentId
}; };
@ -255,16 +249,16 @@ window.FrontendBookApi = window.FrontendBookApi || {};
type: 'GET', type: 'GET',
data: data, data: data,
dataType: 'json' dataType: 'json'
}).done(function (response) { }).done((response) => {
unavailableDatesBackup = response; unavailableDatesBackup = response;
selectedDateStringBackup = selectedDateString; selectedDateStringBackup = selectedDateString;
applyUnavailableDates(response, selectedDateString, true); applyUnavailableDates(response, selectedDateString, true);
}); });
}; }
exports.applyPreviousUnavailableDates = function () { function applyPreviousUnavailableDates() {
applyUnavailableDates(unavailableDatesBackup, selectedDateStringBackup); applyUnavailableDates(unavailableDatesBackup, selectedDateStringBackup);
}; }
function applyUnavailableDates(unavailableDates, selectedDateString, setDate) { function applyUnavailableDates(unavailableDates, selectedDateString, setDate) {
setDate = setDate || false; setDate = setDate || false;
@ -272,16 +266,16 @@ window.FrontendBookApi = window.FrontendBookApi || {};
processingUnavailabilities = true; processingUnavailabilities = true;
// Select first enabled date. // Select first enabled date.
var selectedDateMoment = moment(selectedDateString); const selectedDateMoment = moment(selectedDateString);
var selectedDate = selectedDateMoment.toDate(); const selectedDate = selectedDateMoment.toDate();
var numberOfDays = selectedDateMoment.daysInMonth(); const numberOfDays = selectedDateMoment.daysInMonth();
if (setDate && !GlobalVariables.manageMode) { if (setDate && !App.Vars.manage_mode) {
for (var i = 1; i <= numberOfDays; i++) { for (let i = 1; i <= numberOfDays; i++) {
var currentDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), i); const currentDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), i);
if (unavailableDates.indexOf(moment(currentDate).format('YYYY-MM-DD')) === -1) { if (unavailableDates.indexOf(moment(currentDate).format('YYYY-MM-DD')) === -1) {
$('#select-date').datepicker('setDate', currentDate); $('#select-date').datepicker('setDate', currentDate);
FrontendBookApi.getAvailableHours(moment(currentDate).format('YYYY-MM-DD')); getAvailableHours(moment(currentDate).format('YYYY-MM-DD'));
break; break;
} }
} }
@ -293,7 +287,7 @@ window.FrontendBookApi = window.FrontendBookApi || {};
} }
// Grey out unavailable dates. // Grey out unavailable dates.
$('#select-date .ui-datepicker-calendar td:not(.ui-datepicker-other-month)').each(function (index, td) { $('#select-date .ui-datepicker-calendar td:not(.ui-datepicker-other-month)').each((index, td) => {
selectedDateMoment.set({day: index + 1}); selectedDateMoment.set({day: index + 1});
if (unavailableDates.indexOf(selectedDateMoment.format('YYYY-MM-DD')) !== -1) { if (unavailableDates.indexOf(selectedDateMoment.format('YYYY-MM-DD')) !== -1) {
$(td).addClass('ui-datepicker-unselectable ui-state-disabled'); $(td).addClass('ui-datepicker-unselectable ui-state-disabled');
@ -308,32 +302,41 @@ window.FrontendBookApi = window.FrontendBookApi || {};
* *
* @param {Object} consent Contains user's consents. * @param {Object} consent Contains user's consents.
*/ */
exports.saveConsent = function (consent) { function saveConsent(consent) {
var url = GlobalVariables.baseUrl + '/index.php/consents/ajax_save_consent'; const url = App.Utils.Url.siteUrl('consents/ajax_save_consent');
var data = { const data = {
csrf_token: GlobalVariables.csrfToken, csrf_token: App.Vars.csrf_token,
consent: consent consent: consent
}; };
$.post(url, data); $.post(url, data);
}; }
/** /**
* Delete personal information. * Delete personal information.
* *
* @param {Number} customerToken Customer unique token. * @param {Number} customerToken Customer unique token.
*/ */
exports.deletePersonalInformation = function (customerToken) { function deletePersonalInformation(customerToken) {
var url = GlobalVariables.baseUrl + '/index.php/privacy/ajax_delete_personal_information'; const url = App.Utils.Url.siteUrl('privacy/ajax_delete_personal_information');
var data = { const data = {
csrf_token: GlobalVariables.csrfToken, csrf_token: App.Vars.csrf_token,
customer_token: customerToken customer_token: customerToken
}; };
$.post(url, data).done(function () { $.post(url, data).done(() => {
window.location.href = GlobalVariables.baseUrl; window.location.href = App.Vars.base_url;
}); });
}
return {
registerAppointment,
getAvailableHours,
getUnavailableDates,
applyPreviousUnavailableDates,
saveConsent,
deletePersonalInformation
}; };
})(window.FrontendBookApi); })();

View file

@ -9,53 +9,41 @@
* @since v1.0.0 * @since v1.0.0
* ---------------------------------------------------------------------------- */ * ---------------------------------------------------------------------------- */
window.FrontendBook = window.FrontendBook || {};
/** /**
* Frontend Book * Booking Page
* *
* This module contains functions that implement the book appointment page functionality. Once the "initialize" method * This module contains functions that implement the book appointment page functionality. Once the "initialize" method
* is called the page is fully functional and can serve the appointment booking process. * is called the page is fully functional and can serve the appointment booking process.
* *
* @module FrontendBook * Old Name: FrontendBook
*/ */
(function (exports) { App.Pages.Booking = (function () {
'use strict';
/** /**
* Contains terms and conditions consent. * Contains terms and conditions consent.
* *
* @type {Object} * @type {Object}
*/ */
var termsAndConditionsConsent; let termsAndConditionsConsent;
/** /**
* Contains privacy policy consent. * Contains privacy policy consent.
* *
* @type {Object} * @type {Object}
*/ */
var privacyPolicyConsent; let privacyPolicyConsent;
/** /**
* Determines the functionality of the page. * Determines the functionality of the page.
* *
* @type {Boolean} * @type {Boolean}
*/ */
exports.manageMode = false; let manageMode = false;
/** /**
* This method initializes the book appointment page. * This method initializes the book appointment page.
*
* @param {Boolean} defaultEventHandlers (OPTIONAL) Determines whether the default
* event handlers will be bound to the dom elements.
* @param {Boolean} manageMode (OPTIONAL) Determines whether the customer is going
* to make changes to an existing appointment rather than booking a new one.
*/ */
exports.initialize = function (defaultEventHandlers, manageMode) { function initialize() {
defaultEventHandlers = defaultEventHandlers || true; if (App.Vars.display_cookie_notice) {
manageMode = manageMode || false;
if (GlobalVariables.displayCookieNotice) {
cookieconsent.initialise({ cookieconsent.initialise({
palette: { palette: {
popup: { popup: {
@ -84,12 +72,12 @@ window.FrontendBook = window.FrontendBook || {};
); );
} }
FrontendBook.manageMode = manageMode; manageMode = App.Vars.manage_mode;
// Initialize page's components (tooltips, datepickers etc). // Initialize page's components (tooltips, datepickers etc).
tippy('[data-tippy-content]'); tippy('[data-tippy-content]');
var weekDayId = GeneralFunctions.getWeekDayId(GlobalVariables.firstWeekday); const weekDayId = GeneralFunctions.getWeekDayId(GlobalVariables.firstWeekday);
$('#select-date').datepicker({ $('#select-date').datepicker({
dateFormat: 'dd-mm-yy', dateFormat: 'dd-mm-yy',
@ -144,14 +132,14 @@ window.FrontendBook = window.FrontendBook || {};
closeText: App.Lang.close, closeText: App.Lang.close,
onSelect: function (dateText, instance) { onSelect: function (dateText, instance) {
FrontendBookApi.getAvailableHours(moment($(this).datepicker('getDate')).format('YYYY-MM-DD')); App.Http.Booking.getAvailableHours(moment($(this).datepicker('getDate')).format('YYYY-MM-DD'));
FrontendBook.updateConfirmFrame(); updateConfirmFrame();
}, },
onChangeMonthYear: function (year, month, instance) { onChangeMonthYear: (year, month) => {
var currentDate = new Date(year, month - 1, 1); const currentDate = new Date(year, month - 1, 1);
FrontendBookApi.getUnavailableDates( App.Http.Booking.getUnavailableDates(
$('#select-provider').val(), $('#select-provider').val(),
$('#select-service').val(), $('#select-service').val(),
moment(currentDate).format('YYYY-MM-DD') moment(currentDate).format('YYYY-MM-DD')
@ -162,23 +150,21 @@ window.FrontendBook = window.FrontendBook || {};
$('#select-timezone').val(Intl.DateTimeFormat().resolvedOptions().timeZone); $('#select-timezone').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).
if (defaultEventHandlers) { bindEventHandlers();
bindEventHandlers();
}
// 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 (FrontendBook.manageMode) { if (manageMode) {
applyAppointmentData( applyAppointmentData(
GlobalVariables.appointmentData, GlobalVariables.appointmentData,
GlobalVariables.providerData, GlobalVariables.providerData,
GlobalVariables.customerData GlobalVariables.customerData
); );
} else { } else {
var $selectProvider = $('#select-provider'); const $selectProvider = $('#select-provider');
var $selectService = $('#select-service'); const $selectService = $('#select-service');
// Check if a specific service was selected (via URL parameter). // Check if a specific service was selected (via URL parameter).
var selectedServiceId = GeneralFunctions.getUrlParameter(location.href, 'service'); const selectedServiceId = GeneralFunctions.getUrlParameter(location.href, 'service');
if (selectedServiceId && $selectService.find('option[value="' + selectedServiceId + '"]').length > 0) { if (selectedServiceId && $selectService.find('option[value="' + selectedServiceId + '"]').length > 0) {
$selectService.val(selectedServiceId); $selectService.val(selectedServiceId);
@ -187,12 +173,12 @@ window.FrontendBook = window.FrontendBook || {};
$selectService.trigger('change'); // Load the available hours. $selectService.trigger('change'); // Load the available hours.
// Check if a specific provider was selected. // Check if a specific provider was selected.
var selectedProviderId = GeneralFunctions.getUrlParameter(location.href, 'provider'); const selectedProviderId = GeneralFunctions.getUrlParameter(location.href, 'provider');
if (selectedProviderId && $selectProvider.find('option[value="' + selectedProviderId + '"]').length === 0) { if (selectedProviderId && $selectProvider.find('option[value="' + selectedProviderId + '"]').length === 0) {
// Select a service of this provider in order to make the provider available in the select box. // Select a service of this provider in order to make the provider available in the select box.
for (var index in GlobalVariables.availableProviders) { for (const index in GlobalVariables.availableProviders) {
var provider = GlobalVariables.availableProviders[index]; const provider = GlobalVariables.availableProviders[index];
if (provider.id === selectedProviderId && provider.services.length > 0) { if (provider.id === selectedProviderId && provider.services.length > 0) {
$selectService.val(provider.services[0]).trigger('change'); $selectService.val(provider.services[0]).trigger('change');
@ -204,7 +190,7 @@ window.FrontendBook = window.FrontendBook || {};
$selectProvider.val(selectedProviderId).trigger('change'); $selectProvider.val(selectedProviderId).trigger('change');
} }
} }
}; }
/** /**
* This method binds the necessary event handlers for the book appointments page. * This method binds the necessary event handlers for the book appointments page.
@ -213,16 +199,16 @@ window.FrontendBook = window.FrontendBook || {};
/** /**
* Event: Timezone "Changed" * Event: Timezone "Changed"
*/ */
$('#select-timezone').on('change', function () { $('#select-timezone').on('change', () => {
var date = $('#select-date').datepicker('getDate'); const date = $('#select-date').datepicker('getDate');
if (!date) { if (!date) {
return; return;
} }
FrontendBookApi.getAvailableHours(moment(date).format('YYYY-MM-DD')); App.Http.Booking.getAvailableHours(moment(date).format('YYYY-MM-DD'));
FrontendBook.updateConfirmFrame(); updateConfirmFrame();
}); });
/** /**
@ -230,13 +216,13 @@ window.FrontendBook = window.FrontendBook || {};
* *
* 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', function () { $('#select-provider').on('change', (event) => {
FrontendBookApi.getUnavailableDates( App.Http.Booking.getUnavailableDates(
$(this).val(), $(event.target).val(),
$('#select-service').val(), $('#select-service').val(),
moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD') moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD')
); );
FrontendBook.updateConfirmFrame(); updateConfirmFrame();
}); });
/** /**
@ -245,17 +231,16 @@ window.FrontendBook = window.FrontendBook || {};
* 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', function () { $('#select-service').on('change', (event) => {
var serviceId = $('#select-service').val(); const serviceId = $('#select-service').val();
$('#select-provider').empty(); $('#select-provider').empty();
GlobalVariables.availableProviders.forEach(function (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.
var canServeService = const canServeService =
provider.services.filter(function (providerServiceId) { provider.services.filter((providerServiceId) => Number(providerServiceId) === Number(serviceId))
return Number(providerServiceId) === Number(serviceId); .length > 0;
}).length > 0;
if (canServeService) { if (canServeService) {
$('#select-provider').append( $('#select-provider').append(
@ -271,12 +256,14 @@ window.FrontendBook = window.FrontendBook || {};
); );
} }
FrontendBookApi.getUnavailableDates( App.Http.Booking.getUnavailableDates(
$('#select-provider').val(), $('#select-provider').val(),
$(this).val(), $(event.target).val(),
moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD') moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD')
); );
FrontendBook.updateConfirmFrame();
updateConfirmFrame();
updateServiceDescription(serviceId); updateServiceDescription(serviceId);
}); });
@ -284,16 +271,16 @@ window.FrontendBook = window.FrontendBook || {};
* Event: Next Step Button "Clicked" * Event: Next Step Button "Clicked"
* *
* This handler is triggered every time the user pressed the "next" button on the book wizard. * This handler is triggered every time the user pressed the "next" button on the book wizard.
* Some special tasks might be performed, depending the current wizard step. * Some special tasks might be performed, depending on the current wizard step.
*/ */
$('.button-next').on('click', function () { $('.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. // If we are on the first step and there is not provider selected do not continue with the next step.
if ($(this).attr('data-step_index') === '1' && !$('#select-provider').val()) { if ($(event.target).attr('data-step_index') === '1' && !$('#select-provider').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 ($(this).attr('data-step_index') === '2') { if ($(event.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/>', {
@ -308,15 +295,15 @@ window.FrontendBook = window.FrontendBook || {};
// 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 ($(this).attr('data-step_index') === '3') { if ($(event.target).attr('data-step_index') === '3') {
if (!validateCustomerForm()) { if (!validateCustomerForm()) {
return; // Validation failed, do not continue. return; // Validation failed, do not continue.
} else { } else {
FrontendBook.updateConfirmFrame(); updateConfirmFrame();
var $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) {
var newTermsAndConditionsConsent = { const newTermsAndConditionsConsent = {
first_name: $('#first-name').val(), first_name: $('#first-name').val(),
last_name: $('#last-name').val(), last_name: $('#last-name').val(),
email: $('#email').val(), email: $('#email').val(),
@ -327,13 +314,13 @@ window.FrontendBook = window.FrontendBook || {};
JSON.stringify(newTermsAndConditionsConsent) !== JSON.stringify(termsAndConditionsConsent) JSON.stringify(newTermsAndConditionsConsent) !== JSON.stringify(termsAndConditionsConsent)
) { ) {
termsAndConditionsConsent = newTermsAndConditionsConsent; termsAndConditionsConsent = newTermsAndConditionsConsent;
FrontendBookApi.saveConsent(termsAndConditionsConsent); App.Http.Booking.saveConsent(termsAndConditionsConsent);
} }
} }
var $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) {
var newPrivacyPolicyConsent = { const newPrivacyPolicyConsent = {
first_name: $('#first-name').val(), first_name: $('#first-name').val(),
last_name: $('#last-name').val(), last_name: $('#last-name').val(),
email: $('#email').val(), email: $('#email').val(),
@ -342,19 +329,19 @@ window.FrontendBook = window.FrontendBook || {};
if (JSON.stringify(newPrivacyPolicyConsent) !== JSON.stringify(privacyPolicyConsent)) { if (JSON.stringify(newPrivacyPolicyConsent) !== JSON.stringify(privacyPolicyConsent)) {
privacyPolicyConsent = newPrivacyPolicyConsent; privacyPolicyConsent = newPrivacyPolicyConsent;
FrontendBookApi.saveConsent(privacyPolicyConsent); App.Http.Booking.saveConsent(privacyPolicyConsent);
} }
} }
} }
} }
// Display the next step tab (uses jquery animation effect). // Display the next step tab (uses jquery animation effect).
var nextTabIndex = parseInt($(this).attr('data-step_index')) + 1; const nextTabIndex = parseInt($(event.target).attr('data-step_index')) + 1;
$(this) $(event.target)
.parents() .parents()
.eq(1) .eq(1)
.hide('fade', function () { .hide('fade', () => {
$('.active-step').removeClass('active-step'); $('.active-step').removeClass('active-step');
$('#step-' + nextTabIndex).addClass('active-step'); $('#step-' + nextTabIndex).addClass('active-step');
$('#wizard-frame-' + nextTabIndex).show('fade'); $('#wizard-frame-' + nextTabIndex).show('fade');
@ -367,13 +354,13 @@ window.FrontendBook = window.FrontendBook || {};
* This handler is triggered every time the user pressed the "back" button on the * This handler is triggered every time the user pressed the "back" button on the
* book wizard. * book wizard.
*/ */
$('.button-back').on('click', function () { $('.button-back').on('click', (event) => {
var prevTabIndex = parseInt($(this).attr('data-step_index')) - 1; const prevTabIndex = parseInt($(event.target).attr('data-step_index')) - 1;
$(this) $(event.target)
.parents() .parents()
.eq(1) .eq(1)
.hide('fade', function () { .hide('fade', () => {
$('.active-step').removeClass('active-step'); $('.active-step').removeClass('active-step');
$('#step-' + prevTabIndex).addClass('active-step'); $('#step-' + prevTabIndex).addClass('active-step');
$('#wizard-frame-' + prevTabIndex).show('fade'); $('#wizard-frame-' + prevTabIndex).show('fade');
@ -386,13 +373,13 @@ window.FrontendBook = window.FrontendBook || {};
* 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', function () { $('#available-hours').on('click', '.available-hour', (event) => {
$('.selected-hour').removeClass('selected-hour'); $('.selected-hour').removeClass('selected-hour');
$(this).addClass('selected-hour'); $(event.target).addClass('selected-hour');
FrontendBook.updateConfirmFrame(); updateConfirmFrame();
}); });
if (FrontendBook.manageMode) { if (manageMode) {
/** /**
* Event: Cancel Appointment Button "Click" * Event: Cancel Appointment Button "Click"
* *
@ -402,8 +389,8 @@ window.FrontendBook = window.FrontendBook || {};
* *
* @param {jQuery.Event} event * @param {jQuery.Event} event
*/ */
$('#cancel-appointment').on('click', function (event) { $('#cancel-appointment').on('click', () => {
var buttons = [ const buttons = [
{ {
text: App.Lang.cancel, text: App.Lang.cancel,
click: function () { click: function () {
@ -441,18 +428,18 @@ window.FrontendBook = window.FrontendBook || {};
return false; return false;
}); });
$('#delete-personal-information').on('click', function () { $('#delete-personal-information').on('click', () => {
var buttons = [ const buttons = [
{ {
text: App.Lang.cancel, text: App.Lang.cancel,
click: function () { click: () => {
$('#message-box').dialog('close'); $('#message-box').dialog('close');
} }
}, },
{ {
text: App.Lang.delete, text: App.Lang.delete,
click: function () { click: () => {
FrontendBookApi.deletePersonalInformation(GlobalVariables.customerToken); App.Http.Booking.deletePersonalInformation(GlobalVariables.customerToken);
} }
} }
]; ];
@ -474,22 +461,20 @@ window.FrontendBook = window.FrontendBook || {};
* *
* @param {jQuery.Event} event * @param {jQuery.Event} event
*/ */
$('#book-appointment-submit').on('click', function () { $('#book-appointment-submit').on('click', () => {
FrontendBookApi.registerAppointment(); App.Http.Booking.registerAppointment();
}); });
/** /**
* Event: Refresh captcha image. * Event: Refresh captcha image.
*
* @param {jQuery.Event} event
*/ */
$('.captcha-title button').on('click', function (event) { $('.captcha-title button').on('click', () => {
$('.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', function (event) { $('#select-date').on('mousedown', '.ui-datepicker-calendar td', () => {
setTimeout(function () { setTimeout(() => {
FrontendBookApi.applyPreviousUnavailableDates(); // New jQuery UI version will replace the td elements. App.Http.Booking.applyPreviousUnavailableDates(); // New jQuery UI version will replace the td elements.
}, 300); // There is no draw event unfortunately. }, 300); // There is no draw event unfortunately.
}); });
} }
@ -506,24 +491,26 @@ window.FrontendBook = window.FrontendBook || {};
try { try {
// Validate required fields. // Validate required fields.
var missingRequiredField = false; let missingRequiredField = false;
$('.required').each(function (index, requiredField) {
$('.required').each((index, requiredField) => {
if (!$(requiredField).val()) { if (!$(requiredField).val()) {
$(requiredField).parents('.form-group').addClass('is-invalid'); $(requiredField).parents('.form-group').addClass('is-invalid');
missingRequiredField = true; missingRequiredField = true;
} }
}); });
if (missingRequiredField) { if (missingRequiredField) {
throw new Error(App.Lang.fields_are_required); throw new Error(App.Lang.fields_are_required);
} }
var $acceptToTermsAndConditions = $('#accept-to-terms-and-conditions'); const $acceptToTermsAndConditions = $('#accept-to-terms-and-conditions');
if ($acceptToTermsAndConditions.length && !$acceptToTermsAndConditions.prop('checked')) { if ($acceptToTermsAndConditions.length && !$acceptToTermsAndConditions.prop('checked')) {
$acceptToTermsAndConditions.parents('.form-check').addClass('text-danger'); $acceptToTermsAndConditions.parents('.form-check').addClass('text-danger');
throw new Error(App.Lang.fields_are_required); throw new Error(App.Lang.fields_are_required);
} }
var $acceptToPrivacyPolicy = $('#accept-to-privacy-policy'); const $acceptToPrivacyPolicy = $('#accept-to-privacy-policy');
if ($acceptToPrivacyPolicy.length && !$acceptToPrivacyPolicy.prop('checked')) { if ($acceptToPrivacyPolicy.length && !$acceptToPrivacyPolicy.prop('checked')) {
$acceptToPrivacyPolicy.parents('.form-check').addClass('text-danger'); $acceptToPrivacyPolicy.parents('.form-check').addClass('text-danger');
throw new Error(App.Lang.fields_are_required); throw new Error(App.Lang.fields_are_required);
@ -546,27 +533,27 @@ window.FrontendBook = window.FrontendBook || {};
* Every time this function is executed, it updates the confirmation page with the latest * Every time this function is executed, it updates the confirmation page with the latest
* customer settings and input for the appointment booking. * customer settings and input for the appointment booking.
*/ */
exports.updateConfirmFrame = function () { function updateConfirmFrame() {
if ($('.selected-hour').text() === '') { if ($('.selected-hour').text() === '') {
return; return;
} }
// Appointment Details // Appointment Details
var selectedDate = $('#select-date').datepicker('getDate'); let selectedDate = $('#select-date').datepicker('getDate');
if (selectedDate !== null) { if (selectedDate !== null) {
selectedDate = GeneralFunctions.formatDate(selectedDate, GlobalVariables.dateFormat); selectedDate = App.Utils.Date.format(selectedDate, App.Vars.date_format, App.Vars.time_format);
} }
var serviceId = $('#select-service').val(); const serviceId = $('#select-service').val();
var servicePrice = ''; let servicePrice = '';
var serviceCurrency = ''; let serviceCurrency = '';
GlobalVariables.availableServices.forEach(function (service, index) { App.Vars.available_services.forEach((service) => {
if (Number(service.id) === Number(serviceId) && Number(service.price) > 0) { if (Number(service.id) === Number(serviceId) && Number(service.price) > 0) {
servicePrice = service.price; servicePrice = service.price;
serviceCurrency = service.currency; serviceCurrency = service.currency;
return false; // break loop return false; // Break loop
} }
}); });
@ -607,13 +594,13 @@ window.FrontendBook = window.FrontendBook || {};
}).appendTo('#appointment-details'); }).appendTo('#appointment-details');
// Customer Details // Customer Details
var firstName = GeneralFunctions.escapeHtml($('#first-name').val()); const firstName = GeneralFunctions.escapeHtml($('#first-name').val());
var lastName = GeneralFunctions.escapeHtml($('#last-name').val()); const lastName = GeneralFunctions.escapeHtml($('#last-name').val());
var phoneNumber = GeneralFunctions.escapeHtml($('#phone-number').val()); const phoneNumber = GeneralFunctions.escapeHtml($('#phone-number').val());
var email = GeneralFunctions.escapeHtml($('#email').val()); const email = GeneralFunctions.escapeHtml($('#email').val());
var address = GeneralFunctions.escapeHtml($('#address').val()); const address = GeneralFunctions.escapeHtml($('#address').val());
var city = GeneralFunctions.escapeHtml($('#city').val()); const city = GeneralFunctions.escapeHtml($('#city').val());
var zipCode = GeneralFunctions.escapeHtml($('#zip-code').val()); const zipCode = GeneralFunctions.escapeHtml($('#zip-code').val());
$('#customer-details').empty(); $('#customer-details').empty();
@ -654,7 +641,7 @@ window.FrontendBook = window.FrontendBook || {};
}).appendTo('#customer-details'); }).appendTo('#customer-details');
// Update appointment form data for submission to server when the user confirms the appointment. // Update appointment form data for submission to server when the user confirms the appointment.
var data = {}; const data = {};
data.customer = { data.customer = {
last_name: $('#last-name').val(), last_name: $('#last-name').val(),
@ -680,38 +667,39 @@ window.FrontendBook = window.FrontendBook || {};
id_services: $('#select-service').val() id_services: $('#select-service').val()
}; };
data.manage_mode = FrontendBook.manageMode; data.manage_mode = manageMode;
if (FrontendBook.manageMode) { if (manageMode) {
data.appointment.id = GlobalVariables.appointmentData.id; data.appointment.id = GlobalVariables.appointmentData.id;
data.customer.id = GlobalVariables.customerData.id; data.customer.id = GlobalVariables.customerData.id;
} }
$('input[name="csrfToken"]').val(GlobalVariables.csrfToken); $('input[name="csrfToken"]').val(GlobalVariables.csrfToken);
$('input[name="post_data"]').val(JSON.stringify(data)); $('input[name="post_data"]').val(JSON.stringify(data));
}; }
/** /**
* This method calculates the end datetime of the current appointment. * This method calculates the end datetime of the current appointment.
*
* End datetime is depending on the service and start datetime fields. * End datetime is depending on the service and start datetime fields.
* *
* @return {String} Returns the end datetime in string format. * @return {String} Returns the end datetime in string format.
*/ */
function calculateEndDatetime() { function calculateEndDatetime() {
// Find selected service duration. // Find selected service duration.
var serviceId = $('#select-service').val(); const serviceId = $('#select-service').val();
var service = GlobalVariables.availableServices.find(function (availableService) { const service = App.Vars.available_services.find(
return 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.
var selectedDate = moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD'); const selectedDate = moment($('#select-date').datepicker('getDate')).format('YYYY-MM-DD');
var selectedHour = $('.selected-hour').data('value'); // HH:mm const selectedHour = $('.selected-hour').data('value'); // HH:mm
var startMoment = moment(selectedDate + ' ' + selectedHour); const startMoment = moment(selectedDate + ' ' + selectedHour);
var endMoment; let endMoment;
if (service.duration && startMoment) { if (service.duration && startMoment) {
endMoment = startMoment.clone().add({'minutes': parseInt(service.duration)}); endMoment = startMoment.clone().add({'minutes': parseInt(service.duration)});
@ -739,9 +727,9 @@ window.FrontendBook = window.FrontendBook || {};
$('#select-provider').val(appointment.id_users_provider); $('#select-provider').val(appointment.id_users_provider);
// Set Appointment Date // Set Appointment Date
var startMoment = moment(appointment.start_datetime); const startMoment = moment(appointment.start_datetime);
$('#select-date').datepicker('setDate', startMoment.toDate()); $('#select-date').datepicker('setDate', startMoment.toDate());
FrontendBookApi.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); $('#last-name').val(customer.last_name);
@ -754,10 +742,10 @@ window.FrontendBook = window.FrontendBook || {};
if (customer.timezone) { if (customer.timezone) {
$('#select-timezone').val(customer.timezone); $('#select-timezone').val(customer.timezone);
} }
var appointmentNotes = appointment.notes !== null ? appointment.notes : ''; const appointmentNotes = appointment.notes !== null ? appointment.notes : '';
$('#notes').val(appointmentNotes); $('#notes').val(appointmentNotes);
FrontendBook.updateConfirmFrame(); updateConfirmFrame();
return true; return true;
} catch (exc) { } catch (exc) {
@ -766,18 +754,18 @@ window.FrontendBook = window.FrontendBook || {};
} }
/** /**
* This method updates a div's html content with a brief description of the * This method updates a div's HTML content with a brief description of the
* user selected service (only if available in db). This is useful for the * user selected service (only if available in db). This is useful for the
* customers upon selecting the correct service. * customers upon selecting the correct service.
* *
* @param {Number} serviceId The selected service record id. * @param {Number} serviceId The selected service record id.
*/ */
function updateServiceDescription(serviceId) { function updateServiceDescription(serviceId) {
var $serviceDescription = $('#service-description'); const $serviceDescription = $('#service-description');
$serviceDescription.empty(); $serviceDescription.empty();
var service = GlobalVariables.availableServices.find(function (availableService) { const service = App.Vars.available_services.find(function (availableService) {
return Number(availableService.id) === Number(serviceId); return Number(availableService.id) === Number(serviceId);
}); });
@ -819,4 +807,12 @@ window.FrontendBook = window.FrontendBook || {};
}).appendTo($serviceDescription); }).appendTo($serviceDescription);
} }
} }
})(window.FrontendBook);
document.addEventListener('DOMContentLoaded', initialize);
return {
manageMode,
initialize,
updateConfirmFrame
};
})();