/* ---------------------------------------------------------------------------- * Easy!Appointments - Online Appointment Scheduler * * @package EasyAppointments * @author A.Tselegidis * @copyright Copyright (c) 2013 - 2016, Alex Tselegidis * @license https://opensource.org/licenses/GPL-3.0 - GPLv3 * @link https://easyappointments.org * @since v1.5.0 * ---------------------------------------------------------------------------- */ /** * Working plan utility. * * This module implements the functionality of table calendar view. * * Old Name: BackendCalendarTableView */ App.Utils.CalendarTableView = (function () { const $calendar = $('#calendar'); const $calendarToolbar = $('#calendar-toolbar'); const $calendarFilter = $('#calendar-filter'); const $notification = $('#notification'); const $reloadAppointments = $('#reload-appointments'); const $selectFilterItem = $('#select-filter-item'); const $selectService = $('#select-service'); const $selectProvider = $('#select-provider'); const $appointmentsModal = $('#appointments-modal'); const $unavailabilitiesModal = $('#unavailabilities-modal'); const $header = $('#header'); const $footer = $('#footer'); let $filterProvider; let $filterService; let $selectDate; let $popoverTarget; const moment = window.moment; let lastFocusedEventData; /** * Add the utility event listeners. */ function addEventListeners() { $calendar.on('click', '.calendar-header .btn.previous', () => { const dayInterval = $selectFilterItem.val(); const currentDate = $selectDate[0]._flatpickr.selectedDates[0]; const startDate = moment(currentDate).subtract(1, 'days'); const endDate = startDate.clone().add(dayInterval - 1, 'days'); $selectDate[0]._flatpickr.setDate(startDate.toDate()); createView(startDate.toDate(), endDate.toDate()); }); $calendar.on('click', '.calendar-header .btn.next', () => { const dayInterval = $selectFilterItem.val(); const currentDate = $selectDate[0]._flatpickr.selectedDates[0]; const startDate = moment(currentDate).add(1, 'days'); const endDate = startDate.clone().add(dayInterval - 1, 'days'); $selectDate[0]._flatpickr.setDate(startDate.toDate()); createView(startDate.toDate(), endDate.toDate()); }); $calendarToolbar.on('change', '#select-filter-item', () => { const dayInterval = $selectFilterItem.val(); const currentDate = $selectDate[0]._flatpickr.selectedDates[0]; const startDate = moment(currentDate); const endDate = startDate.clone().add(dayInterval - 1, 'days'); createView(startDate.toDate(), endDate.toDate()); }); $calendarToolbar.on('click', '#reload-appointments', () => { // Fetch the events and place them in the existing HTML format. const dayInterval = $selectFilterItem.val(); const currentDate = $selectDate[0]._flatpickr.selectedDates[0]; const startDateMoment = moment(currentDate); const startDate = startDateMoment.toDate(); const endDateMoment = startDateMoment.clone().add(dayInterval - 1, 'days'); const endDate = endDateMoment.toDate(); App.Http.Calendar.getCalendarAppointmentsForTableView(startDate, endDate).done((response) => { let currentDate = startDate; while (currentDate <= endDate) { $('.calendar-view .date-column').each((index, dateColumn) => { const $dateColumn = $(dateColumn); const date = new Date($dateColumn.data('date')); if (moment(currentDate).format('YYYY-MM-DD') !== moment(date).format('YYYY-MM-DD')) { return true; } $dateColumn .find('.date-column-title') .text(App.Utils.Date.format(date, vars('date_format'), vars('time_format'))); $dateColumn.find('.provider-column').each((index, providerColumn) => { const $providerColumn = $(providerColumn); const provider = $providerColumn.data('provider'); $providerColumn .find('.calendar-wrapper') .data('fullCalendar') .getEventSources() .forEach((eventSource) => eventSource.remove()); createNonWorkingHours( $providerColumn.find('.calendar-wrapper'), $providerColumn.data('provider'), ); // Add the appointments to the column. /** @var {Array} response.appointments */ createAppointments($providerColumn, response.appointments); // Add the unavailabilities to the column. /** @var {Array} response.unavailabilities */ createUnavailabilities($providerColumn, response.unavailabilities); // Add the blocked periods to the column. createBlockedPeriods($providerColumn, response.blocked_periods); // Add the provider breaks to the column. const workingPlan = JSON.parse(provider.settings.working_plan); const day = moment(date).format('dddd').toLowerCase(); if (workingPlan[day]) { const breaks = workingPlan[day].breaks; createBreaks($providerColumn, breaks); } }); }); currentDate = moment(currentDate).add({days: 1}).toDate(); } }); }); /** * Event: On Window Resize */ $(window).on('resize', () => { setCalendarViewSize(); }); /** * Event: Popover Close Button "Click" * * Hides the open popover element. */ $calendar.on('click', '.close-popover', (event) => { if ($popoverTarget) { $popoverTarget.popover('dispose'); } }); /** * Event: Popover Edit Button "Click" * * Enables the edit dialog of the selected table event. */ $calendar.on('click', '.edit-popover', (event) => { if ($popoverTarget) { $popoverTarget.popover('dispose'); } let startMoment; let endMoment; if (lastFocusedEventData.extendedProps.data.workingPlanException) { const date = lastFocusedEventData.extendedProps.data.date; const workingPlanException = lastFocusedEventData.extendedProps.data.workingPlanException; const provider = lastFocusedEventData.extendedProps.data.provider; App.Components.WorkingPlanExceptionsModal.edit(date, workingPlanException).done( (date, workingPlanException) => { const successCallback = () => { App.Layouts.Backend.displayNotification(lang('working_plan_exception_saved')); const workingPlanExceptions = JSON.parse(provider.settings.working_plan_exceptions) || {}; workingPlanExceptions[date] = workingPlanException; for (const index in vars('available_providers')) { const availableProvider = vars('available_providers')[index]; if (Number(availableProvider.id) === Number(provider.id)) { availableProvider.settings.working_plan_exceptions = JSON.stringify(workingPlanExceptions); break; } } $reloadAppointments.trigger('click'); // Update the calendar. }; App.Http.Calendar.saveWorkingPlanException( date, workingPlanException, provider.id, successCallback, null, ); }, ); } else if (lastFocusedEventData.extendedProps.data.is_unavailability === false) { const appointment = lastFocusedEventData.extendedProps.data; App.Components.AppointmentsModal.resetModal(); // Apply appointment data and show modal dialog. $appointmentsModal.find('.modal-header h3').text(lang('edit_appointment_title')); $appointmentsModal.find('#appointment-id').val(appointment.id); $appointmentsModal.find('#select-service').val(appointment.id_services).trigger('change'); $appointmentsModal.find('#select-provider').val(appointment.id_users_provider); // Set the start and end datetime of the appointment. startMoment = moment(appointment.start_datetime); $appointmentsModal.find('#start-datetime')[0]._flatpickr.setDate(startMoment.toDate()); endMoment = moment(appointment.end_datetime); $appointmentsModal.find('#end-datetime')[0]._flatpickr.setDate(endMoment.toDate()); const customer = appointment.customer; $appointmentsModal.find('#customer-id').val(appointment.id_users_customer); $appointmentsModal.find('#first-name').val(customer.first_name); $appointmentsModal.find('#last-name').val(customer.last_name); $appointmentsModal.find('#email').val(customer.email); $appointmentsModal.find('#phone-number').val(customer.phone_number); $appointmentsModal.find('#address').val(customer.address); $appointmentsModal.find('#city').val(customer.city); $appointmentsModal.find('#zip-code').val(customer.zip_code); $appointmentsModal.find('#language').val(customer.language); $appointmentsModal.find('#timezone').val(customer.timezone); $appointmentsModal.find('#appointment-location').val(appointment.location); $appointmentsModal.find('#appointment-status').val(appointment.status); $appointmentsModal.find('#appointment-notes').val(appointment.notes); $appointmentsModal.find('#customer-notes').val(customer.notes); $appointmentsModal.find('#custom-field-1').val(customer.custom_field_1); $appointmentsModal.find('#custom-field-2').val(customer.custom_field_2); $appointmentsModal.find('#custom-field-3').val(customer.custom_field_3); $appointmentsModal.find('#custom-field-4').val(customer.custom_field_4); $appointmentsModal.find('#custom-field-5').val(customer.custom_field_5); App.Components.ColorSelection.setColor( $appointmentsModal.find('#appointment-color'), appointment.color, ); $appointmentsModal.modal('show'); } else { const unavailability = lastFocusedEventData.extendedProps.data; // Replace string date values with actual date objects. unavailability.start_datetime = moment(lastFocusedEventData.start).format('YYYY-MM-DD HH:mm:ss'); startMoment = moment(unavailability.start_datetime); unavailability.end_datetime = moment(lastFocusedEventData.end).format('YYYY-MM-DD HH:mm:ss'); endMoment = moment(unavailability.end_datetime); App.Components.UnavailabilitiesModal.resetModal(); // Apply unavailability data to dialog. $unavailabilitiesModal.find('.modal-header h3').text(lang('edit_unavailability_title')); $unavailabilitiesModal.find('#unavailability-start')[0]._flatpickr.setDate(startMoment.toDate()); $unavailabilitiesModal.find('#unavailability-id').val(unavailability.id); $unavailabilitiesModal.find('#unavailability-provider').val(unavailability.id_users_provider); $unavailabilitiesModal.find('#unavailability-end')[0]._flatpickr.setDate(endMoment.toDate()); $unavailabilitiesModal.find('#unavailability-notes').val(unavailability.notes); $unavailabilitiesModal.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', (event) => { if ($popoverTarget) { $popoverTarget.popover('dispose'); } // If id_role parameter exists the popover is an working plan exception. if (lastFocusedEventData.extendedProps.data.hasOwnProperty('id_roles')) { // Do not display confirmation prompt. const date = moment(lastFocusedEventData.start).format('YYYY-MM-DD'); const providerId = lastFocusedEventData.extendedProps.data.id; App.Http.Calendar.deleteWorkingPlanException(date, providerId).done(() => { $('#message-box').dialog('close'); const workingPlanExceptions = JSON.parse( lastFocusedEventData.extendedProps.data.settings.working_plan_exceptions, ); delete workingPlanExceptions[moment(lastFocusedEventData.start).format('YYYY-MM-DD')]; lastFocusedEventData.extendedProps.data.settings.working_plan_exceptions = JSON.stringify(workingPlanExceptions); // Refresh calendar event items. $reloadAppointments.trigger('click'); }); } else if (!lastFocusedEventData.extendedProps.data.is_unavailability) { const buttons = [ { text: lang('cancel'), click: (event, messageModal) => { messageModal.dispose(); }, }, { text: lang('delete'), click: (event, messageModal) => { const appointmentId = lastFocusedEventData.extendedProps.data.id; const cancellationReason = $('#cancellation-reason').val(); App.Http.Calendar.deleteAppointment(appointmentId, cancellationReason).done(() => { messageModal.dispose(); // Refresh calendar event items. $reloadAppointments.trigger('click'); }); }, }, ]; App.Utils.Message.show( lang('delete_appointment_title'), lang('write_appointment_removal_reason'), buttons, ); $('