/* ---------------------------------------------------------------------------- * Easy!Appointments - Online Appointment Scheduler * * @package EasyAppointments * @author A.Tselegidis * @copyright Copyright (c) Alex Tselegidis * @license https://opensource.org/licenses/GPL-3.0 - GPLv3 * @link https://easyappointments.org * @since v1.5.0 * ---------------------------------------------------------------------------- */ /** * Customers page. * * This module implements the functionality of the customers page. */ App.Pages.Customers = (function () { const $customers = $('#customers'); const $filterCustomers = $('#filter-customers'); const $id = $('#customer-id'); const $firstName = $('#first-name'); const $lastName = $('#last-name'); const $email = $('#email'); const $phoneNumber = $('#phone-number'); const $address = $('#address'); const $city = $('#city'); const $zipCode = $('#zip-code'); const $timezone = $('#timezone'); const $language = $('#language'); const $notes = $('#notes'); const $formMessage = $('#form-message'); const $customerAppointments = $('#customer-appointments'); let filterResults = {}; let filterLimit = 20; /** * Add the page event listeners. */ function addEventListeners() { /** * Event: Filter Customers Form "Submit" * * @param {jQuery.Event} event */ $customers.on('submit', '#filter-customers form', (event) => { event.preventDefault(); const key = $filterCustomers.find('.key').val(); $filterCustomers.find('.selected').removeClass('selected'); filterLimit = 20; resetForm(); filter(key); }); /** * Event: Filter Entry "Click" * * Display the customer data of the selected row. * * @param {jQuery.Event} event */ $customers.on('click', '.customer-row', (event) => { if ($filterCustomers.find('.filter').prop('disabled')) { return; // Do nothing when user edits a customer record. } const customerId = $(event.currentTarget).attr('data-id'); const customer = filterResults.find((filterResult) => Number(filterResult.id) === Number(customerId)); display(customer); $('#filter-customers .selected').removeClass('selected'); $(event.currentTarget).addClass('selected'); $('#edit-customer, #delete-customer').prop('disabled', false); }); /** * Event: Add Customer Button "Click" */ $customers.on('click', '#add-customer', () => { resetForm(); $customers.find('#add-edit-delete-group').hide(); $customers.find('#save-cancel-group').show(); $customers.find('.record-details').find('input, select, textarea').prop('disabled', false); $customers.find('.record-details .form-label span').prop('hidden', false); $filterCustomers.find('button').prop('disabled', true); $filterCustomers.find('.results').css('color', '#AAA'); }); /** * Event: Edit Customer Button "Click" */ $customers.on('click', '#edit-customer', () => { $customers.find('.record-details').find('input, select, textarea').prop('disabled', false); $customers.find('.record-details .form-label span').prop('hidden', false); $customers.find('#add-edit-delete-group').hide(); $customers.find('#save-cancel-group').show(); $filterCustomers.find('button').prop('disabled', true); $filterCustomers.find('.results').css('color', '#AAA'); }); /** * Event: Cancel Customer Add/Edit Operation Button "Click" */ $customers.on('click', '#cancel-customer', () => { const id = $id.val(); resetForm(); if (id) { select(id, true); } }); /** * Event: Save Add/Edit Customer Operation "Click" */ $customers.on('click', '#save-customer', () => { const customer = { first_name: $firstName.val(), last_name: $lastName.val(), email: $email.val(), phone_number: $phoneNumber.val(), address: $address.val(), city: $city.val(), zip_code: $zipCode.val(), notes: $notes.val(), timezone: $timezone.val(), language: $language.val() || 'english' }; if ($id.val()) { customer.id = $id.val(); } if (!validate()) { return; } save(customer); }); /** * Event: Delete Customer Button "Click" */ $customers.on('click', '#delete-customer', () => { const customerId = $id.val(); const buttons = [ { text: lang('cancel'), click: () => { $('#message-box').dialog('close'); } }, { text: lang('delete'), click: () => { remove(customerId); $('#message-box').dialog('close'); } } ]; App.Utils.Message.show(lang('delete_customer'), lang('delete_record_prompt'), buttons); }); } /** * Save a customer record to the database (via ajax post). * * @param {Object} customer Contains the customer data. */ function save(customer) { App.Http.Customers.save(customer).then((response) => { App.Layouts.Backend.displayNotification(lang('customer_saved')); resetForm(); $('#filter-customers .key').val(''); filter('', response.id, true); }); } /** * Delete a customer record from database. * * @param {Number} id Record id to be deleted. */ function remove(id) { App.Http.Customers.destroy(id).then(() => { App.Layouts.Backend.displayNotification(lang('customer_deleted')); resetForm(); filter($('#filter-customers .key').val()); }); } /** * Validate customer data before save (insert or update). */ function validate() { $formMessage.removeClass('alert-danger').hide(); $('.is-invalid').removeClass('is-invalid'); try { // Validate required fields. let missingRequired = false; $('.required').each((index, requiredField) => { if ($(requiredField).val() === '') { $(requiredField).addClass('is-invalid'); missingRequired = true; } }); if (missingRequired) { throw new Error(lang('fields_are_required')); } // Validate email address. const email = $email.val(); if (email && !App.Utils.Validation.email(email)) { $email.addClass('is-invalid'); throw new Error(lang('invalid_email')); } // Validate phone number. const phoneNumber = $phoneNumber.val(); if (phoneNumber && !App.Utils.Validation.phone(phoneNumber)) { $phoneNumber.addClass('is-invalid'); throw new Error(lang('invalid_phone')); } return true; } catch (error) { $formMessage.addClass('alert-danger').text(error.message).show(); return false; } } /** * Bring the customer form back to its initial state. */ function resetForm() { $customers.find('.record-details').find('input, select, textarea').val('').prop('disabled', true); $customers.find('.record-details .form-label span').prop('hidden', true); $customers.find('.record-details #timezone').val('UTC'); $language.val('english'); $customerAppointments.empty(); $customers.find('#edit-customer, #delete-customer').prop('disabled', true); $customers.find('#add-edit-delete-group').show(); $customers.find('#save-cancel-group').hide(); $customers.find('.record-details .is-invalid').removeClass('is-invalid'); $customers.find('.record-details #form-message').hide(); $filterCustomers.find('button').prop('disabled', false); $filterCustomers.find('.selected').removeClass('selected'); $filterCustomers.find('.results').css('color', ''); } /** * Display a customer record into the form. * * @param {Object} customer Contains the customer record data. */ function display(customer) { $id.val(customer.id); $firstName.val(customer.first_name); $lastName.val(customer.last_name); $email.val(customer.email); $phoneNumber.val(customer.phone_number); $address.val(customer.address); $city.val(customer.city); $zipCode.val(customer.zip_code); $notes.val(customer.notes); $timezone.val(customer.timezone); $language.val(customer.language || 'english'); $customerAppointments.empty(); if (!customer.appointments.length) { $('

', { 'text': lang('no_records_found') }).appendTo($customerAppointments); } customer.appointments.forEach((appointment) => { if ( vars('role_slug') === App.Layouts.Backend.DB_SLUG_PROVIDER && parseInt(appointment.id_users_provider) !== vars('user_id') ) { return; } if ( vars('role_slug') === App.Layouts.Backend.DB_SLUG_SECRETARY && vars('secretary_providers').indexOf(appointment.id_users_provider) === -1 ) { return; } const start = App.Utils.Date.format( moment(appointment.start_datetime).toDate(), vars('date_format'), vars('time_format'), true ); const end = App.Utils.Date.format( moment(appointment.end_datetime).toDate(), vars('date_format'), vars('time_format'), true ); $('

', { 'class': 'appointment-row', 'data-id': appointment.id, 'html': [ // Service - Provider $('', { 'href': App.Utils.Url.siteUrl(`calendar/reschedule/${appointment.hash}`), 'html': [ $('', { 'class': 'fas fa-edit me-1' }), $('', { 'text': appointment.service.name + ' - ' + appointment.provider.first_name + ' ' + appointment.provider.last_name }), $('
') ] }), // Start $('', { 'text': start }), $('
'), // End $('', { 'text': end }), $('
'), // Timezone $('', { 'text': vars('timezones')[appointment.provider.timezone] }) ] }).appendTo('#customer-appointments'); }); } /** * Filter customer records. * * @param {String} keyword This keyword string is used to filter the customer records. * @param {Number} selectId Optional, if set then after the filter operation the record with the given * ID will be selected (but not displayed). * @param {Boolean} show Optional (false), if true then the selected record will be displayed on the form. */ function filter(keyword, selectId = null, show = false) { App.Http.Customers.search(keyword, filterLimit).then((response) => { filterResults = response; $filterCustomers.find('.results').empty(); response.forEach((customer) => { $('#filter-customers .results').append(getFilterHtml(customer)).append($('
')); }); if (!response.length) { $filterCustomers.find('.results').append( $('', { 'text': lang('no_records_found') }) ); } else if (response.length === filterLimit) { $('