2016-04-27 22:57:11 +03:00
|
|
|
/* ----------------------------------------------------------------------------
|
|
|
|
* Easy!Appointments - Open Source Web Scheduler
|
|
|
|
*
|
|
|
|
* @package EasyAppointments
|
|
|
|
* @author A.Tselegidis <alextselegidis@gmail.com>
|
2021-12-18 19:43:45 +03:00
|
|
|
* @copyright Copyright (c) Alex Tselegidis
|
|
|
|
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
|
|
|
|
* @link https://easyappointments.org
|
2016-04-27 22:57:11 +03:00
|
|
|
* @since v1.0.0
|
|
|
|
* ---------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/**
|
2022-01-14 11:26:44 +03:00
|
|
|
* Booking HTTP client.
|
2016-04-27 22:57:11 +03:00
|
|
|
*
|
2022-01-14 11:26:44 +03:00
|
|
|
* This module implements the booking related HTTP requests.
|
2016-04-27 22:57:11 +03:00
|
|
|
*
|
2022-01-13 13:33:40 +03:00
|
|
|
* Old Name: FrontendBookApi
|
2016-04-27 22:57:11 +03:00
|
|
|
*/
|
2022-01-13 13:33:40 +03:00
|
|
|
App.Http.Booking = (function () {
|
2022-01-17 07:14:36 +03:00
|
|
|
const $selectService = $('#select-service');
|
|
|
|
const $selectProvider = $('#select-provider');
|
|
|
|
const $availableHours = $('#available-hours');
|
|
|
|
const $captchaHint = $('#captcha-hint');
|
|
|
|
const $captchaTitle = $('.captcha-title');
|
2022-01-13 13:33:40 +03:00
|
|
|
let unavailableDatesBackup;
|
|
|
|
let selectedDateStringBackup;
|
|
|
|
let processingUnavailabilities = false;
|
2016-07-19 00:23:53 +03:00
|
|
|
|
2016-04-27 22:57:11 +03:00
|
|
|
/**
|
2016-05-14 13:09:21 +03:00
|
|
|
* Get Available Hours
|
2016-04-27 22:57:11 +03:00
|
|
|
*
|
2016-05-14 13:09:21 +03:00
|
|
|
* This function makes an AJAX call and returns the available hours for the selected service,
|
|
|
|
* provider and date.
|
|
|
|
*
|
2020-08-31 17:35:27 +03:00
|
|
|
* @param {String} selectedDate The selected date of the available hours we need.
|
2016-04-27 22:57:11 +03:00
|
|
|
*/
|
2022-01-13 13:33:40 +03:00
|
|
|
function getAvailableHours(selectedDate) {
|
2022-01-17 07:14:36 +03:00
|
|
|
$availableHours.empty();
|
2016-04-27 22:57:11 +03:00
|
|
|
|
2020-05-06 20:15:11 +03:00
|
|
|
// Find the selected service duration (it is going to be send within the "data" object).
|
2022-01-17 07:14:36 +03:00
|
|
|
const serviceId = $selectService.val();
|
2020-05-06 20:15:11 +03:00
|
|
|
|
|
|
|
// Default value of duration (in minutes).
|
2022-01-13 13:33:40 +03:00
|
|
|
let serviceDuration = 15;
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-18 10:18:22 +03:00
|
|
|
const service = vars('available_services').find(
|
2022-01-13 13:33:40 +03:00
|
|
|
(availableService) => Number(availableService.id) === Number(serviceId)
|
|
|
|
);
|
2020-05-06 20:15:11 +03:00
|
|
|
|
|
|
|
if (service) {
|
|
|
|
serviceDuration = service.duration;
|
|
|
|
}
|
2016-04-27 22:57:11 +03:00
|
|
|
|
2016-05-14 13:09:21 +03:00
|
|
|
// If the manage mode is true then the appointment's start date should return as available too.
|
2022-01-18 10:18:22 +03:00
|
|
|
const appointmentId = App.Pages.Booking.manageMode ? vars('appointment_data').id : null;
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
// Make ajax post request and get the available hours.
|
2022-01-17 21:44:44 +03:00
|
|
|
const url = App.Utils.Url.siteUrl('booking/get_available_hours');
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const data = {
|
2022-01-18 10:18:22 +03:00
|
|
|
csrf_token: vars('csrf_token'),
|
2022-01-17 07:14:36 +03:00
|
|
|
service_id: $selectService.val(),
|
|
|
|
provider_id: $selectProvider.val(),
|
2020-08-31 17:35:27 +03:00
|
|
|
selected_date: selectedDate,
|
2020-05-06 20:15:11 +03:00
|
|
|
service_duration: serviceDuration,
|
2022-01-13 13:33:40 +03:00
|
|
|
manage_mode: App.Pages.Booking.manageMode,
|
2016-07-15 21:52:21 +03:00
|
|
|
appointment_id: appointmentId
|
|
|
|
};
|
2016-04-27 22:57:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
$.post(url, data).done((response) => {
|
2021-11-06 19:38:37 +03:00
|
|
|
// The response contains the available hours for the selected provider and service. Fill the available
|
|
|
|
// hours div with response data.
|
|
|
|
if (response.length > 0) {
|
2022-01-17 07:14:36 +03:00
|
|
|
let providerId = $selectProvider.val();
|
2021-11-06 19:38:37 +03:00
|
|
|
|
|
|
|
if (providerId === 'any-provider') {
|
2022-01-18 10:18:22 +03:00
|
|
|
for (const availableProvider of vars('available_providers')) {
|
2021-11-06 19:38:37 +03:00
|
|
|
if (availableProvider.services.indexOf(Number(serviceId)) !== -1) {
|
|
|
|
providerId = availableProvider.id; // Use first available provider.
|
|
|
|
break;
|
2021-01-27 15:39:12 +03:00
|
|
|
}
|
2020-05-06 20:15:11 +03:00
|
|
|
}
|
2021-11-06 19:38:37 +03:00
|
|
|
}
|
2020-04-08 11:57:18 +03:00
|
|
|
|
2022-01-18 10:18:22 +03:00
|
|
|
const provider = vars('available_providers').find(
|
2022-01-13 13:33:40 +03:00
|
|
|
(availableProvider) => Number(providerId) === Number(availableProvider.id)
|
|
|
|
);
|
2020-03-29 17:20:30 +03:00
|
|
|
|
2021-11-06 19:38:37 +03:00
|
|
|
if (!provider) {
|
|
|
|
throw new Error('Could not find provider.');
|
|
|
|
}
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const providerTimezone = provider.timezone;
|
|
|
|
const selectedTimezone = $('#select-timezone').val();
|
2022-01-18 10:18:22 +03:00
|
|
|
const timeFormat = vars('time_format') === 'regular' ? 'h:mm a' : 'HH:mm';
|
2020-03-29 17:20:30 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
response.forEach((availableHour) => {
|
|
|
|
const availableHourMoment = moment
|
2021-11-06 19:38:37 +03:00
|
|
|
.tz(selectedDate + ' ' + availableHour + ':00', providerTimezone)
|
|
|
|
.tz(selectedTimezone);
|
2021-10-28 14:30:39 +03:00
|
|
|
|
2021-11-06 19:38:37 +03:00
|
|
|
if (availableHourMoment.format('YYYY-MM-DD') !== selectedDate) {
|
|
|
|
return; // Due to the selected timezone the available hour belongs to another date.
|
2016-04-27 22:57:11 +03:00
|
|
|
}
|
|
|
|
|
2022-01-17 07:14:36 +03:00
|
|
|
$availableHours.append(
|
2021-11-06 19:38:37 +03:00
|
|
|
$('<button/>', {
|
2021-11-23 10:33:43 +03:00
|
|
|
'class': 'btn btn-outline-secondary w-100 shadow-none available-hour',
|
2021-11-06 19:38:37 +03:00
|
|
|
'data': {
|
|
|
|
'value': availableHour
|
|
|
|
},
|
|
|
|
'text': availableHourMoment.format(timeFormat)
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
if (App.Pages.Booking.manageMode) {
|
2021-11-06 19:38:37 +03:00
|
|
|
// Set the appointment's start time as the default selection.
|
|
|
|
$('.available-hour')
|
|
|
|
.removeClass('selected-hour')
|
2022-01-13 13:33:40 +03:00
|
|
|
.filter(
|
|
|
|
(index, availableHourEl) =>
|
|
|
|
$(availableHourEl).text() ===
|
2022-01-18 10:18:22 +03:00
|
|
|
moment(vars('appointment_data').start_datetime).format(timeFormat)
|
2022-01-13 13:33:40 +03:00
|
|
|
)
|
2021-11-06 19:38:37 +03:00
|
|
|
.addClass('selected-hour');
|
|
|
|
} else {
|
|
|
|
// Set the first available hour as the default selection.
|
|
|
|
$('.available-hour:eq(0)').addClass('selected-hour');
|
2021-07-19 16:02:45 +03:00
|
|
|
}
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
App.Pages.Booking.updateConfirmFrame();
|
2021-11-06 19:38:37 +03:00
|
|
|
}
|
|
|
|
|
2022-01-17 07:14:36 +03:00
|
|
|
if (!$availableHours.find('.available-hour').length) {
|
2022-01-18 10:22:25 +03:00
|
|
|
$availableHours.text(lang('no_available_hours'));
|
2021-11-06 19:38:37 +03:00
|
|
|
}
|
|
|
|
});
|
2022-01-13 13:33:40 +03:00
|
|
|
}
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2022-01-13 13:33:40 +03:00
|
|
|
function registerAppointment() {
|
|
|
|
const $captchaText = $('.captcha-text');
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
if ($captchaText.length > 0) {
|
2021-11-23 12:10:09 +03:00
|
|
|
$captchaText.removeClass('is-invalid');
|
2016-04-27 22:57:11 +03:00
|
|
|
if ($captchaText.val() === '') {
|
2021-11-23 12:10:09 +03:00
|
|
|
$captchaText.addClass('is-invalid');
|
2016-04-27 22:57:11 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const formData = JSON.parse($('input[name="post_data"]').val());
|
2020-12-09 22:28:26 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const data = {
|
2022-01-18 10:18:22 +03:00
|
|
|
csrf_token: vars('csrf_token'),
|
2016-07-15 21:52:21 +03:00
|
|
|
post_data: formData
|
|
|
|
};
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
if ($captchaText.length > 0) {
|
2020-05-06 20:15:11 +03:00
|
|
|
data.captcha = $captchaText.val();
|
2016-04-27 22:57:11 +03:00
|
|
|
}
|
|
|
|
|
2022-01-18 10:18:22 +03:00
|
|
|
if (vars('manage_mode')) {
|
|
|
|
data.exclude_appointment_id = vars('appointment_data').id;
|
2016-04-27 22:57:11 +03:00
|
|
|
}
|
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const url = App.Utils.Url.siteUrl('booking/register');
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const $layer = $('<div/>');
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
$.ajax({
|
2020-05-06 20:15:11 +03:00
|
|
|
url: url,
|
2016-04-27 22:57:11 +03:00
|
|
|
method: 'post',
|
2020-05-06 20:15:11 +03:00
|
|
|
data: data,
|
2016-04-27 22:57:11 +03:00
|
|
|
dataType: 'json',
|
2022-01-13 13:33:40 +03:00
|
|
|
beforeSend: () => {
|
2021-11-06 19:38:37 +03:00
|
|
|
$layer.appendTo('body').css({
|
|
|
|
background: 'white',
|
|
|
|
position: 'fixed',
|
|
|
|
top: '0',
|
|
|
|
left: '0',
|
|
|
|
height: '100vh',
|
|
|
|
width: '100vw',
|
|
|
|
opacity: '0.5'
|
|
|
|
});
|
2016-04-27 22:57:11 +03:00
|
|
|
}
|
|
|
|
})
|
2022-01-13 13:33:40 +03:00
|
|
|
.done((response) => {
|
2016-04-27 22:57:11 +03:00
|
|
|
if (response.captcha_verification === false) {
|
2022-01-18 10:22:25 +03:00
|
|
|
$captchaHint.text(lang('captcha_is_wrong')).fadeTo(400, 1);
|
2016-04-27 22:57:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
setTimeout(() => {
|
2022-01-17 07:14:36 +03:00
|
|
|
$captchaHint.fadeTo(400, 0);
|
2016-04-27 22:57:11 +03:00
|
|
|
}, 3000);
|
|
|
|
|
2022-01-17 07:14:36 +03:00
|
|
|
$captchaTitle.find('button').trigger('click');
|
2016-04-27 22:57:11 +03:00
|
|
|
|
2021-11-23 12:10:09 +03:00
|
|
|
$captchaText.addClass('is-invalid');
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
window.location.href = App.Utils.Url.siteUrl('booking_confirmation/of/' + response.appointment_hash);
|
2016-04-27 22:57:11 +03:00
|
|
|
})
|
2022-01-13 13:33:40 +03:00
|
|
|
.fail(() => {
|
2022-01-17 07:14:36 +03:00
|
|
|
$captchaTitle.find('button').trigger('click');
|
2016-04-27 22:57:11 +03:00
|
|
|
})
|
2022-01-13 13:33:40 +03:00
|
|
|
.always(() => {
|
2016-04-27 22:57:11 +03:00
|
|
|
$layer.remove();
|
|
|
|
});
|
2022-01-13 13:33:40 +03:00
|
|
|
}
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the unavailable dates of a provider.
|
|
|
|
*
|
|
|
|
* This method will fetch the unavailable dates of the selected provider and service and then it will
|
2016-05-14 13:09:21 +03:00
|
|
|
* select the first available date (if any). It uses the "FrontendBookApi.getAvailableHours" method to
|
|
|
|
* fetch the appointment* hours of the selected date.
|
2016-04-27 22:57:11 +03:00
|
|
|
*
|
2016-05-14 13:09:21 +03:00
|
|
|
* @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.
|
2016-04-27 22:57:11 +03:00
|
|
|
*/
|
2022-01-13 13:33:40 +03:00
|
|
|
function getUnavailableDates(providerId, serviceId, selectedDateString) {
|
2016-10-10 17:45:19 +03:00
|
|
|
if (processingUnavailabilities) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-16 11:16:48 +03:00
|
|
|
if (!providerId || !serviceId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-18 10:18:22 +03:00
|
|
|
const appointmentId = App.Pages.Booking.manageMode ? vars('appointment_data').id : null;
|
2018-04-23 16:58:04 +03:00
|
|
|
|
2022-01-17 21:44:44 +03:00
|
|
|
const url = App.Utils.Url.siteUrl('booking/get_unavailable_dates');
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const data = {
|
2016-07-15 21:52:21 +03:00
|
|
|
provider_id: providerId,
|
|
|
|
service_id: serviceId,
|
|
|
|
selected_date: encodeURIComponent(selectedDateString),
|
2022-01-18 10:18:22 +03:00
|
|
|
csrf_token: vars('csrf_token'),
|
2022-01-13 13:33:40 +03:00
|
|
|
manage_mode: App.Pages.Booking.manageMode,
|
2018-04-23 16:58:04 +03:00
|
|
|
appointment_id: appointmentId
|
2016-07-15 21:52:21 +03:00
|
|
|
};
|
2016-04-27 22:57:11 +03:00
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
url: url,
|
|
|
|
type: 'GET',
|
|
|
|
data: data,
|
|
|
|
dataType: 'json'
|
2022-01-13 13:33:40 +03:00
|
|
|
}).done((response) => {
|
2021-11-06 19:38:37 +03:00
|
|
|
unavailableDatesBackup = response;
|
|
|
|
selectedDateStringBackup = selectedDateString;
|
|
|
|
applyUnavailableDates(response, selectedDateString, true);
|
|
|
|
});
|
2022-01-13 13:33:40 +03:00
|
|
|
}
|
2016-04-27 22:57:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
function applyPreviousUnavailableDates() {
|
2020-05-06 20:23:49 +03:00
|
|
|
applyUnavailableDates(unavailableDatesBackup, selectedDateStringBackup);
|
2022-01-13 13:33:40 +03:00
|
|
|
}
|
2016-07-19 00:23:53 +03:00
|
|
|
|
2020-05-06 20:23:49 +03:00
|
|
|
function applyUnavailableDates(unavailableDates, selectedDateString, setDate) {
|
2016-07-19 00:23:53 +03:00
|
|
|
setDate = setDate || false;
|
|
|
|
|
2016-10-10 17:45:19 +03:00
|
|
|
processingUnavailabilities = true;
|
|
|
|
|
2016-07-19 00:23:53 +03:00
|
|
|
// Select first enabled date.
|
2022-01-13 13:33:40 +03:00
|
|
|
const selectedDateMoment = moment(selectedDateString);
|
|
|
|
const selectedDate = selectedDateMoment.toDate();
|
|
|
|
const numberOfDays = selectedDateMoment.daysInMonth();
|
2016-07-19 00:23:53 +03:00
|
|
|
|
2022-01-18 10:18:22 +03:00
|
|
|
if (setDate && !vars('manage_mode')) {
|
2022-01-13 13:33:40 +03:00
|
|
|
for (let i = 1; i <= numberOfDays; i++) {
|
|
|
|
const currentDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), i);
|
2021-11-25 10:40:48 +03:00
|
|
|
if (unavailableDates.indexOf(moment(currentDate).format('YYYY-MM-DD')) === -1) {
|
2016-07-19 00:23:53 +03:00
|
|
|
$('#select-date').datepicker('setDate', currentDate);
|
2022-01-13 13:33:40 +03:00
|
|
|
getAvailableHours(moment(currentDate).format('YYYY-MM-DD'));
|
2016-07-19 00:23:53 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If all the days are unavailable then hide the appointments hours.
|
|
|
|
if (unavailableDates.length === numberOfDays) {
|
2022-01-18 10:22:25 +03:00
|
|
|
$availableHours.text(lang('no_available_hours'));
|
2016-07-19 00:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Grey out unavailable dates.
|
2022-01-13 13:33:40 +03:00
|
|
|
$('#select-date .ui-datepicker-calendar td:not(.ui-datepicker-other-month)').each((index, td) => {
|
2021-11-25 10:40:48 +03:00
|
|
|
selectedDateMoment.set({day: index + 1});
|
|
|
|
if (unavailableDates.indexOf(selectedDateMoment.format('YYYY-MM-DD')) !== -1) {
|
2016-07-19 00:23:53 +03:00
|
|
|
$(td).addClass('ui-datepicker-unselectable ui-state-disabled');
|
|
|
|
}
|
|
|
|
});
|
2016-10-10 17:45:19 +03:00
|
|
|
|
|
|
|
processingUnavailabilities = false;
|
2016-07-19 00:23:53 +03:00
|
|
|
}
|
|
|
|
|
2018-06-24 18:27:16 +03:00
|
|
|
/**
|
|
|
|
* Save the user's consent.
|
|
|
|
*
|
|
|
|
* @param {Object} consent Contains user's consents.
|
|
|
|
*/
|
2022-01-13 13:33:40 +03:00
|
|
|
function saveConsent(consent) {
|
2022-01-17 21:44:44 +03:00
|
|
|
const url = App.Utils.Url.siteUrl('consents/save_consent');
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const data = {
|
2022-01-18 10:18:22 +03:00
|
|
|
csrf_token: vars('csrf_token'),
|
2018-06-24 18:27:16 +03:00
|
|
|
consent: consent
|
|
|
|
};
|
|
|
|
|
2020-12-02 20:57:49 +03:00
|
|
|
$.post(url, data);
|
2022-01-13 13:33:40 +03:00
|
|
|
}
|
2018-06-24 18:27:16 +03:00
|
|
|
|
2018-06-24 20:08:45 +03:00
|
|
|
/**
|
|
|
|
* Delete personal information.
|
|
|
|
*
|
|
|
|
* @param {Number} customerToken Customer unique token.
|
|
|
|
*/
|
2022-01-13 13:33:40 +03:00
|
|
|
function deletePersonalInformation(customerToken) {
|
2022-01-17 21:44:44 +03:00
|
|
|
const url = App.Utils.Url.siteUrl('privacy/delete_personal_information');
|
2020-05-06 20:15:11 +03:00
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
const data = {
|
2022-01-18 10:18:22 +03:00
|
|
|
csrf_token: vars('csrf_token'),
|
2018-06-24 20:08:45 +03:00
|
|
|
customer_token: customerToken
|
|
|
|
};
|
|
|
|
|
2022-01-13 13:33:40 +03:00
|
|
|
$.post(url, data).done(() => {
|
2022-01-18 10:18:22 +03:00
|
|
|
window.location.href = vars('base_url');
|
2021-11-06 19:38:37 +03:00
|
|
|
});
|
2022-01-13 13:33:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
registerAppointment,
|
|
|
|
getAvailableHours,
|
|
|
|
getUnavailableDates,
|
|
|
|
applyPreviousUnavailableDates,
|
|
|
|
saveConsent,
|
|
|
|
deletePersonalInformation
|
2018-06-24 20:08:45 +03:00
|
|
|
};
|
2022-01-13 13:33:40 +03:00
|
|
|
})();
|