/* ---------------------------------------------------------------------------- * 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 * ---------------------------------------------------------------------------- */ window.BackendCalendarTableView = window.BackendCalendarTableView || {}; /** * Backend Calendar * * This module implements the table calendar view of backend. * * @module BackendCalendarTableView */ (function (exports) { 'use strict'; /** * Sticky Table Header Fix * * @type {Number} */ var stickyTableHeaderInterval; /** * Bind page event handlers. */ function _bindEventHandlers() { var $calendarToolbar = $('#calendar-toolbar'); var $calendar = $('#calendar'); $calendar.on('click', '.calendar-header .btn.previous', function () { var endDate = new Date($('#calendar .date-column:first').data('date')).add({days: -1}); var startDate = new Date(endDate.getTime()).add({days: -1 * (parseInt($('#select-filter-item').val()) - 1)}); $('.select-date').datepicker('setDate', startDate); _createView(startDate, endDate); }); $calendar.on('click', '.calendar-header .btn.next', function () { var startDate = new Date($('#calendar .date-column:last').data('date')).add({days: 1}); var endDate = new Date(startDate.getTime()).add({days: parseInt($('#select-filter-item').val()) - 1}); $('.select-date').datepicker('setDate', startDate); _createView(startDate, endDate); }); $calendarToolbar.on('change', '#select-filter-item', function () { var startDate = new Date($('.calendar-view .date-column:first').data('date')); var endDate = new Date(startDate.getTime()).add({days: parseInt($(this).val()) - 1}); _createView(startDate, endDate); // Horizontal scrolling fix for sticky table headers. stickyTableHeaderInterval = setInterval(function () { $(window).trigger('resize.stickyTableHeaders'); }, 1000); }); $calendarToolbar.on('click', '#reload-appointments', function () { // Remove all the events from the tables. $('.calendar-view .event').remove(); // Fetch the events and place them in the existing HTML format. var startDate = new Date($('.calendar-view .date-column:first').data('date')); var endDate = new Date($('.calendar-view .date-column:last').data('date')); _getCalendarEvents(startDate, endDate) .done(function (response) { var currentDate = startDate; while (currentDate <= endDate) { $('.calendar-view .date-column').each(function (index, dateColumn) { var $dateColumn = $(dateColumn); var date = new Date($dateColumn.data('date')); if (currentDate.getTime() !== date.getTime()) { return true; } $(dateColumn).find('.provider-column').each(function (index, providerColumn) { var $providerColumn = $(providerColumn); var provider = $providerColumn.data('provider'); // Add the appointments to the column. _createAppointments($providerColumn, response.appointments); // Add the unavailabilities to the column. _createUnavailabilities($providerColumn, response.unavailabilities); // Add the provider breaks to the column. var workingPlan = JSON.parse(provider.settings.working_plan); var day = date.toString('dddd').toLowerCase(); if (workingPlan[day]) { var breaks = workingPlan[day].breaks; _createBreaks($providerColumn, breaks); } }); }); currentDate.add({days: 1}); } _setCalendarSize(); Backend.placeFooterToBottom(); }) .fail(GeneralFunctions.ajaxFailureHandler); }); $calendar.on('click', '.calendar-view table td', function () { if ($(this).index() === 0) { return; // Clicked on an hour slot. } // Open the appointments modal in the selected hour. var hour = $(this).parent().find('td:first').text().split(':'); var date = new Date($(this).parents('.date-column').data('date')); date.set({hour: parseInt(hour[0]), minute: parseInt(hour[1])}); // Open the appointments dialog. $('#insert-appointment').trigger('click'); // Update start date field. $('#start-datetime').datepicker('setDate', date); // Select Service and provider. var $providerColumn = $(this).parents('.provider-column'); var serviceId = $providerColumn.find('thead tr:last th').eq($(this).index()).data('id'); var providerId = $providerColumn.data('provider').id; $('#select-service').val(serviceId).trigger('change'); $('#select-provider').val(providerId).trigger('change'); }); var lastFocusedEventData; /** * Event: On Table Event Click * * @param {jQuery.Event} event */ $calendar.on('click', '.event', function (event) { event.stopPropagation(); if ($(this).hasClass('break')) { return; // Do nothing with break events. } $('.popover').remove(); // Close all open popovers. var html; var entry = $(this).data(); if ($(this).hasClass('unavailability')) { var notes = 'Notes ' + entry.notes; html = '' + '' + EALang.start + ' ' + GeneralFunctions.formatDate(entry.start_datetime, GlobalVariables.dateFormat, true) + '
' + '' + EALang.end + ' ' + GeneralFunctions.formatDate(entry.end_datetime, GlobalVariables.dateFormat, true) + '
' + notes + '
' + '
' + '' + '' + '' + '
'; } else { html = '' + '' + EALang.start + ' ' + GeneralFunctions.formatDate(entry.start_datetime, GlobalVariables.dateFormat, true) + '
' + '' + EALang.end + ' ' + GeneralFunctions.formatDate(entry.end_datetime, GlobalVariables.dateFormat, true) + '
' + '' + EALang.service + ' ' + entry.service.name + '
' + '' + EALang.provider + ' ' + entry.provider.first_name + ' ' + entry.provider.last_name + '
' + '' + EALang.customer + ' ' + entry.customer.first_name + ' ' + entry.customer.last_name + '
' + '
' + '' + '' + '' + '
'; } var title = entry.is_unavailable !== '0' ? EALang.unavailable : entry.service.name + ' - ' + entry.customer.first_name + ' ' + entry.customer.last_name; $(event.target).popover({ placement: 'top', title: title, content: html, html: true, container: '#calendar', trigger: 'manual' }); lastFocusedEventData = entry; $(event.target).popover('toggle'); // Fix popover position if ($('.popover').length > 0 && $('.popover').position().top < 200) { $('.popover').css('top', '200px'); } }); /** * Event: On Window Resize */ $(window).on('resize', _setCalendarSize); /** * Event: Popover Close Button "Click" * * Hides the open popover element. */ $calendar.on('click', '.close-popover', function () { $(this).parents().eq(2).popover('destroy'); }); /** * Event: Popover Edit Button "Click" * * Enables the edit dialog of the selected table event. */ $calendar.on('click', '.edit-popover', function () { $(this).parents().eq(2).popover('destroy'); // Hide the popover var $dialog; if (lastFocusedEventData.is_unavailable == false) { var appointment = lastFocusedEventData; $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-notes').val(appointment.notes); $dialog.find('#customer-notes').val(customer.notes); } else { var unavailable = lastFocusedEventData; // Replace string date values with actual date objects. unavailable.start_datetime = Date.parse(lastFocusedEventData.start_datetime); unavailable.end_datetime = Date.parse(lastFocusedEventData.end_datetime); $dialog = $('#manage-unavailable'); BackendCalendarUnavailabilitiesModal.resetUnavailableDialog(); // Apply unvailable data to dialog. $dialog.find('.modal-header h3').text('Edit Unavailable Period'); $dialog.find('#unavailable-start').datetimepicker('setDate', unavailable.start_datetime); $dialog.find('#unavailable-id').val(unavailable.id); $dialog.find('#unavailable-provider').val(unavailable.id_users_provider); $dialog.find('#unavailable-end').datetimepicker('setDate', unavailable.end_datetime); $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. */ $calendar.on('click', '.delete-popover', function () { $(this).parents().eq(2).popover('destroy'); // Hide the popover if (lastFocusedEventData.is_unavailable == false) { var buttons = [ { text: 'OK', click: function () { var postUrl = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_delete_appointment'; var postData = { csrfToken: GlobalVariables.csrfToken, appointment_id: lastFocusedEventData.id, delete_reason: $('#delete-reason').val() }; $.post(postUrl, postData, function (response) { $('#message_box').dialog('close'); // Refresh calendar event items. $('#select-filter-item').trigger('change'); }, 'json').fail(GeneralFunctions.ajaxFailureHandler); } }, { text: EALang.cancel, click: function () { $('#message_box').dialog('close'); } } ]; GeneralFunctions.displayMessageBox(EALang.delete_appointment_title, EALang.write_appointment_removal_reason, buttons); var $formGroup = $('
', { 'class': 'form-group' }) .appendTo('#message_box'); $('