/* ---------------------------------------------------------------------------- * Easy!Appointments - Open Source Web Scheduler * * @package EasyAppointments * @author A.Tselegidis <alextselegidis@gmail.com> * @copyright Copyright (c) 2013 - 2020, Alex Tselegidis * @license http://opensource.org/licenses/GPL-3.0 - GPLv3 * @link http://easyappointments.org * @since v1.0.0 * ---------------------------------------------------------------------------- */ window.FrontendBookApi = window.FrontendBookApi || {}; /** * Frontend Book API * * This module serves as the API consumer for the booking wizard of the app. * * @module FrontendBookApi */ (function (exports) { 'use strict'; var unavailableDatesBackup; var selectedDateStringBackup; var processingUnavailabilities = false; /** * Get Available Hours * * This function makes an AJAX call and returns the available hours for the selected service, * provider and date. * * @param {String} selectedDate The selected date of the available hours we need. */ exports.getAvailableHours = function (selectedDate) { $('#available-hours').empty(); // Find the selected service duration (it is going to be send within the "data" object). var serviceId = $('#select-service').val(); // Default value of duration (in minutes). var serviceDuration = 15; var service = GlobalVariables.availableServices.find(function (availableService) { return Number(availableService.id) === Number(serviceId); }); if (service) { serviceDuration = service.duration; } // 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; // Make ajax post request and get the available hours. var url = GlobalVariables.baseUrl + '/index.php/booking/ajax_get_available_hours'; var data = { csrfToken: GlobalVariables.csrfToken, service_id: $('#select-service').val(), provider_id: $('#select-provider').val(), selected_date: selectedDate, service_duration: serviceDuration, manage_mode: FrontendBook.manageMode, appointment_id: appointmentId }; $.post(url, data).done(function (response) { // The response contains the available hours for the selected provider and service. Fill the available // hours div with response data. if (response.length > 0) { var providerId = $('#select-provider').val(); if (providerId === 'any-provider') { for (var availableProvider of GlobalVariables.availableProviders) { if (availableProvider.services.indexOf(Number(serviceId)) !== -1) { providerId = availableProvider.id; // Use first available provider. break; } } } var provider = GlobalVariables.availableProviders.find(function (availableProvider) { return Number(providerId) === Number(availableProvider.id); }); if (!provider) { throw new Error('Could not find provider.'); } var providerTimezone = provider.timezone; var selectedTimezone = $('#select-timezone').val(); var timeFormat = GlobalVariables.timeFormat === 'regular' ? 'h:mm a' : 'HH:mm'; response.forEach(function (availableHour) { var availableHourMoment = moment .tz(selectedDate + ' ' + availableHour + ':00', providerTimezone) .tz(selectedTimezone); if (availableHourMoment.format('YYYY-MM-DD') !== selectedDate) { return; // Due to the selected timezone the available hour belongs to another date. } $('#available-hours').append( $('<button/>', { 'class': 'btn btn-outline-secondary w-100 shadow-none available-hour', 'data': { 'value': availableHour }, 'text': availableHourMoment.format(timeFormat) }) ); }); if (FrontendBook.manageMode) { // Set the appointment's start time as the default selection. $('.available-hour') .removeClass('selected-hour') .filter(function () { return ( $(this).text() === moment(GlobalVariables.appointmentData.start_datetime).format(timeFormat) ); }) .addClass('selected-hour'); } else { // Set the first available hour as the default selection. $('.available-hour:eq(0)').addClass('selected-hour'); } FrontendBook.updateConfirmFrame(); } if (!$('.available-hour').length) { $('#available-hours').text(EALang.no_available_hours); } }); }; /** * Register an appointment to the database. * * This method will make an ajax call to the appointments controller that will register * the appointment to the database. */ exports.registerAppointment = function () { var $captchaText = $('.captcha-text'); if ($captchaText.length > 0) { $captchaText.removeClass('is-invalid'); if ($captchaText.val() === '') { $captchaText.addClass('is-invalid'); return; } } var formData = JSON.parse($('input[name="post_data"]').val()); var data = { csrfToken: GlobalVariables.csrfToken, post_data: formData }; if ($captchaText.length > 0) { data.captcha = $captchaText.val(); } if (GlobalVariables.manageMode) { data.exclude_appointment_id = GlobalVariables.appointmentData.id; } var url = GlobalVariables.baseUrl + '/index.php/booking/ajax_register_appointment'; var $layer = $('<div/>'); $.ajax({ url: url, method: 'post', data: data, dataType: 'json', beforeSend: function (jqxhr, settings) { $layer.appendTo('body').css({ background: 'white', position: 'fixed', top: '0', left: '0', height: '100vh', width: '100vw', opacity: '0.5' }); } }) .done(function (response) { if (response.captcha_verification === false) { $('#captcha-hint').text(EALang.captcha_is_wrong).fadeTo(400, 1); setTimeout(function () { $('#captcha-hint').fadeTo(400, 0); }, 3000); $('.captcha-title button').trigger('click'); $captchaText.addClass('is-invalid'); return false; } window.location.href = GlobalVariables.baseUrl + '/index.php/appointments/book_success/' + response.appointment_hash; }) .fail(function (jqxhr, textStatus, errorThrown) { $('.captcha-title button').trigger('click'); }) .always(function () { $layer.remove(); }); }; /** * Get the unavailable dates of a provider. * * This method will fetch the unavailable dates of the selected provider and service and then it will * select the first available date (if any). It uses the "FrontendBookApi.getAvailableHours" method to * fetch the appointment* hours of the selected date. * * @param {Number} providerId The selected provider ID. * @param {Number} serviceId The selected service ID. * @param {String} selectedDateString Y-m-d value of the selected date. */ exports.getUnavailableDates = function (providerId, serviceId, selectedDateString) { if (processingUnavailabilities) { return; } if (!providerId || !serviceId) { return; } var appointmentId = FrontendBook.manageMode ? GlobalVariables.appointmentData.id : null; var url = GlobalVariables.baseUrl + '/index.php/booking/ajax_get_unavailable_dates'; var data = { provider_id: providerId, service_id: serviceId, selected_date: encodeURIComponent(selectedDateString), csrfToken: GlobalVariables.csrfToken, manage_mode: FrontendBook.manageMode, appointment_id: appointmentId }; $.ajax({ url: url, type: 'GET', data: data, dataType: 'json' }).done(function (response) { unavailableDatesBackup = response; selectedDateStringBackup = selectedDateString; applyUnavailableDates(response, selectedDateString, true); }); }; exports.applyPreviousUnavailableDates = function () { applyUnavailableDates(unavailableDatesBackup, selectedDateStringBackup); }; function applyUnavailableDates(unavailableDates, selectedDateString, setDate) { setDate = setDate || false; processingUnavailabilities = true; // Select first enabled date. var selectedDateMoment = moment(selectedDateString); var selectedDate = selectedDateMoment.toDate(); var numberOfDays = selectedDateMoment.daysInMonth(); if (setDate && !GlobalVariables.manageMode) { for (var i = 1; i <= numberOfDays; i++) { var currentDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), i); if (unavailableDates.indexOf(moment(currentDate).format('YYYY-MM-DD')) === -1) { $('#select-date').datepicker('setDate', currentDate); FrontendBookApi.getAvailableHours(moment(currentDate).format('YYYY-MM-DD')); break; } } } // If all the days are unavailable then hide the appointments hours. if (unavailableDates.length === numberOfDays) { $('#available-hours').text(EALang.no_available_hours); } // Grey out unavailable dates. $('#select-date .ui-datepicker-calendar td:not(.ui-datepicker-other-month)').each(function (index, td) { selectedDateMoment.set({day: index + 1}); if (unavailableDates.indexOf(selectedDateMoment.format('YYYY-MM-DD')) !== -1) { $(td).addClass('ui-datepicker-unselectable ui-state-disabled'); } }); processingUnavailabilities = false; } /** * Save the user's consent. * * @param {Object} consent Contains user's consents. */ exports.saveConsent = function (consent) { var url = GlobalVariables.baseUrl + '/index.php/consents/ajax_save_consent'; var data = { csrfToken: GlobalVariables.csrfToken, consent: consent }; $.post(url, data); }; /** * Delete personal information. * * @param {Number} customerToken Customer unique token. */ exports.deletePersonalInformation = function (customerToken) { var url = GlobalVariables.baseUrl + '/index.php/privacy/ajax_delete_personal_information'; var data = { csrfToken: GlobalVariables.csrfToken, customer_token: customerToken }; $.post(url, data).done(function () { window.location.href = GlobalVariables.baseUrl; }); }; })(window.FrontendBookApi);