/* ---------------------------------------------------------------------------- * Easy!Appointments - Open Source Web Scheduler * * @package EasyAppointments * @author A.Tselegidis * @copyright Copyright (c) 2013 - 2020, Alex Tselegidis * @license http://opensource.org/licenses/GPL-3.0 - GPLv3 * @link http://easyappointments.org * @since v1.2.0 * ---------------------------------------------------------------------------- */ /** * Backend Calendar * * This module implements the default calendar view of backend. * * @module BackendCalendarDefaultView */ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {}; (function (exports) { 'use strict'; // Constants var FILTER_TYPE_PROVIDER = 'provider'; var FILTER_TYPE_SERVICE = 'service'; // Variables var lastFocusedEventData; // Contains event data for later use. /** * Bind event handlers for the calendar view. */ function bindEventHandlers() { var $calendarPage = $('#calendar-page'); /** * Event: Reload Button "Click" * * When the user clicks the reload button an the calendar items need to be refreshed. */ $('#reload-appointments').click(function () { refreshCalendarAppointments( $('#calendar'), $('#select-filter-item').val(), $('#select-filter-item').find('option:selected').attr('type'), $('#calendar').fullCalendar('getView').start, $('#calendar').fullCalendar('getView').end); }); /** * Event: Popover Close Button "Click" * * Hides the open popover element. */ $calendarPage.on('click', '.close-popover', function () { $(this).parents('.popover').popover('dispose'); }); /** * Event: Popover Edit Button "Click" * * Enables the edit dialog of the selected calendar event. */ $calendarPage.on('click', '.edit-popover', function () { $(this).parents('.popover').popover('dispose'); var $dialog; if (lastFocusedEventData.data.is_unavailable === '0') { var appointment = lastFocusedEventData.data; $dialog = $('#manage-appointment'); BackendCalendarAppointmentsModal.resetAppointmentDialog(); // Apply appointment data and show modal dialog. $dialog.find('.modal-header h3').text(EALang.edit_appointment_title); $dialog.find('#appointment-id').val(appointment.id); $dialog.find('#select-service').val(appointment.id_services).trigger('change'); $dialog.find('#select-provider').val(appointment.id_users_provider); // Set the start and end datetime of the appointment. var startDatetime = Date.parseExact(appointment.start_datetime, 'yyyy-MM-dd HH:mm:ss'); $dialog.find('#start-datetime').datetimepicker('setDate', startDatetime); var endDatetime = Date.parseExact(appointment.end_datetime, 'yyyy-MM-dd HH:mm:ss'); $dialog.find('#end-datetime').datetimepicker('setDate', endDatetime); var customer = appointment.customer; $dialog.find('#customer-id').val(appointment.id_users_customer); $dialog.find('#first-name').val(customer.first_name); $dialog.find('#last-name').val(customer.last_name); $dialog.find('#email').val(customer.email); $dialog.find('#phone-number').val(customer.phone_number); $dialog.find('#address').val(customer.address); $dialog.find('#city').val(customer.city); $dialog.find('#zip-code').val(customer.zip_code); $dialog.find('#appointment-location').val(appointment.location); $dialog.find('#appointment-notes').val(appointment.notes); $dialog.find('#customer-notes').val(customer.notes); } else { var unavailable = lastFocusedEventData.data; // Replace string date values with actual date objects. unavailable.start_datetime = lastFocusedEventData.start.format('YYYY-MM-DD HH:mm:ss'); var startDatetime = Date.parseExact(unavailable.start_datetime, 'yyyy-MM-dd HH:mm:ss'); unavailable.end_datetime = lastFocusedEventData.end.format('YYYY-MM-DD HH:mm:ss'); var endDatetime = Date.parseExact(unavailable.end_datetime, 'yyyy-MM-dd HH:mm:ss'); $dialog = $('#manage-unavailable'); BackendCalendarUnavailabilitiesModal.resetUnavailableDialog(); // Apply unavailable data to dialog. $dialog.find('.modal-header h3').text('Edit Unavailable Period'); $dialog.find('#unavailable-start').datetimepicker('setDate', startDatetime); $dialog.find('#unavailable-id').val(unavailable.id); $dialog.find('#unavailable-provider').val(unavailable.id_users_provider); $dialog.find('#unavailable-end').datetimepicker('setDate', endDatetime); $dialog.find('#unavailable-notes').val(unavailable.notes); } // :: DISPLAY EDIT DIALOG $dialog.modal('show'); }); /** * Event: Popover Delete Button "Click" * * Displays a prompt on whether the user wants the appointment to be deleted. If he confirms the * deletion then an AJAX call is made to the server and deletes the appointment from the database. */ $calendarPage.on('click', '.delete-popover', function () { $(this).parents('.popover').popover('dispose'); var url; var data; // If id_role parameter exists the popover is an extra working day. if (lastFocusedEventData.data.hasOwnProperty('id_roles')) { // Do not display confirmation prompt. url = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_delete_extra_period'; data = { csrfToken: GlobalVariables.csrfToken, extra_period: lastFocusedEventData.start.format('YYYY-MM-DD'), provider_id: lastFocusedEventData.data.id }; $.post(url, data) .done(function () { $('#message_box').dialog('close'); var extraWorkingPlan = jQuery.parseJSON(lastFocusedEventData.data.settings.extra_working_plan); delete extraWorkingPlan[lastFocusedEventData.start.format('YYYY-MM-DD')]; lastFocusedEventData.data.settings.extra_working_plan = JSON.stringify(extraWorkingPlan); // Refresh calendar event items. $('#select-filter-item').trigger('change'); }) .fail(GeneralFunctions.ajaxFailureHandler); } else if (lastFocusedEventData.data.is_unavailable === '0') { var buttons = [ { text: 'OK', click: function () { url = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_delete_appointment'; data = { csrfToken: GlobalVariables.csrfToken, appointment_id: lastFocusedEventData.data.id, delete_reason: $('#delete-reason').val() }; $.post(url, data) .done(function () { $('#message_box').dialog('close'); // Refresh calendar event items. $('#select-filter-item').trigger('change'); }) .fail(GeneralFunctions.ajaxFailureHandler); } }, { text: EALang.cancel, click: function () { $('#message_box').dialog('close'); } } ]; GeneralFunctions.displayMessageBox(EALang.delete_appointment_title, EALang.write_appointment_removal_reason, buttons); $('#message_box').append(''); $('#delete-reason').css('width', '100%'); } else { // Do not display confirmation prompt. url = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_delete_unavailable'; data = { csrfToken: GlobalVariables.csrfToken, unavailable_id: lastFocusedEventData.data.id }; $.post(url, data) .done(function () { $('#message_box').dialog('close'); // Refresh calendar event items. $('#select-filter-item').trigger('change'); }) .fail(GeneralFunctions.ajaxFailureHandler); } }); /** * Event: Calendar Filter Item "Change" * * Load the appointments that correspond to the select filter item and display them on the calendar. */ $('#select-filter-item').change(function () { // If current value is service, then the sync buttons must be disabled. if ($('#select-filter-item option:selected').attr('type') === FILTER_TYPE_SERVICE) { $('#google-sync, #enable-sync, #insert-appointment, #insert-dropdown').prop('disabled', true); $('#calendar').fullCalendar('option', 'selectable', false); $('#calendar').fullCalendar('option', 'editable', false); } else { $('#google-sync, #enable-sync, #insert-appointment, #insert-dropdown').prop('disabled', false); $('#calendar').fullCalendar('option', 'selectable', true); $('#calendar').fullCalendar('option', 'editable', true); var providerId = $('#select-filter-item').val(); var provider = GlobalVariables.availableProviders.find(function(availableProvider) { return Number(availableProvider.id) === Number(providerId); }); if (provider && provider.timezone) { $('.provider-timezone').text(GlobalVariables.timezones[provider.timezone]); } // If the user has already the sync enabled then apply the proper style changes. if ($('#select-filter-item option:selected').attr('google-sync') === 'true') { $('#enable-sync').addClass('btn-danger enabled'); $('#enable-sync span:eq(1)').text(EALang.disable_sync); $('#google-sync').prop('disabled', false); } else { $('#enable-sync').removeClass('btn-danger enabled'); $('#enable-sync span:eq(1)').text(EALang.enable_sync); $('#google-sync').prop('disabled', true); } } }); } /** * Get Calendar Component Height * * This method calculates the proper calendar height, in order to be displayed correctly, even when the * browser window is resizing. * * @return {Number} Returns the calendar element height in pixels. */ function getCalendarHeight() { var result = window.innerHeight - $('#footer').outerHeight() - $('#header').outerHeight() - $('#calendar-toolbar').outerHeight() - 60; // 60 for fine tuning return (result > 500) ? result : 500; // Minimum height is 500px } /** * Get the event notes for the popup widget. * * @param {Event} event */ function getEventNotes(event) { if (!event.data || !event.data.notes) { return '-'; } var notes = event.data.notes; return notes.length > 100 ? notes.substring(0, 100) + '...' : notes; } /** * Calendar Event "Click" Callback * * When the user clicks on an appointment object on the calendar, then a data preview popover is display * above the calendar item. */ function calendarEventClick(event, jsEvent, view) { $('.popover').popover('dispose'); // Close all open popovers. var $html; var displayEdit; var displayDelete; // Depending where the user clicked the event (title or empty space) we // need to use different selectors to reach the parent element. var $parent = $(jsEvent.target.offsetParent); var $altParent = $(jsEvent.target).parents().eq(1); if ($(this).hasClass('fc-unavailable') || $parent.hasClass('fc-unavailable') || $altParent.hasClass('fc-unavailable')) { displayEdit = (($parent.hasClass('fc-custom') || $altParent.hasClass('fc-custom')) && GlobalVariables.user.privileges.appointments.edit === true) ? 'mr-2' : 'd-none'; displayDelete = (($parent.hasClass('fc-custom') || $altParent.hasClass('fc-custom')) && GlobalVariables.user.privileges.appointments.delete === true) ? 'mr-2' : 'd-none'; // Same value at the time. $html = $('
', { 'html': [ $('', { 'class': 'd-inline-block mr-2', 'text': EALang.start }), $('', { 'text': GeneralFunctions.formatDate(event.start.format('YYYY-MM-DD HH:mm:ss'), GlobalVariables.dateFormat, true) }), $('
'), $('', { 'class': 'd-inline-block mr-2', 'text': EALang.end }), $('', { 'text': GeneralFunctions.formatDate(event.end.format('YYYY-MM-DD HH:mm:ss'), GlobalVariables.dateFormat, true) }), $('
'), $('', { 'text': EALang.notes }), $('', { 'text': getEventNotes(event) }), $('
'), $('
'), $('
', { 'class': 'd-flex justify-content-center', 'html': [ $('