From bd6cab36f0b47a576f575c3921a7e4b80ed36f6c Mon Sep 17 00:00:00 2001 From: "alextselegidis@gmail.com" Date: Tue, 2 Jul 2013 17:18:19 +0000 Subject: [PATCH] Added user friendly display of exceptions, raised on php (need to apply this methodology to the rest of the code). --- src/application/controllers/appointments.php | 382 +++++++++--------- .../helpers/custom_exceptions_helper.php | 51 ++- src/application/views/appointments/book.php | 13 + .../views/appointments/book_success.php | 28 +- src/assets/js/frontend_book.js | 240 ++++++----- src/assets/js/general_functions.js | 49 +++ 6 files changed, 440 insertions(+), 323 deletions(-) diff --git a/src/application/controllers/appointments.php b/src/application/controllers/appointments.php index 7c4c1856..6689de37 100644 --- a/src/application/controllers/appointments.php +++ b/src/application/controllers/appointments.php @@ -19,59 +19,64 @@ class Appointments extends CI_Controller { $this->load->model('Settings_Model'); if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') { - $available_services = $this->Services_Model->get_available_services(); - $available_providers = $this->Providers_Model->get_available_providers(); - $company_name = $this->Settings_Model->get_setting('company_name'); + try { + $available_services = $this->Services_Model->get_available_services(); + $available_providers = $this->Providers_Model->get_available_providers(); + $company_name = $this->Settings_Model->get_setting('company_name'); - // If an appointment hash is provided then it means that the customer - // is trying to edit a registered record. - if ($appointment_hash !== ''){ - // Load the appointments data and set the manage mode of the page. - $manage_mode = TRUE; - - $results = $this->Appointments_Model - ->get_batch(array('hash' => $appointment_hash)); - - if (count($results) === 0) { - // The requested appointment doesn't exist in the database. Display - // a message to the customer. - $view_data = array( - 'message_title' => 'Appointment Not Found!', - 'message_text' => 'The appointment you requested does not exist in the ' - . 'database anymore.', - 'message_icon' => $this->config->item('base_url') . 'assets/images/error.png' - ); + // If an appointment hash is provided then it means that the customer + // is trying to edit a registered appointment record. + if ($appointment_hash !== ''){ + // Load the appointments data and enable the manage mode of the page. + $manage_mode = TRUE; + + $appointments_results = $this->Appointments_Model + ->get_batch(array('hash' => $appointment_hash)); - $this->load->view('appointments/message', $view_data); - return; - } - - // Php 5.3 does not support treating a function result as an array. - $appointment_data = $results[0]; - - $provider_data = $this->Providers_Model - ->get_row($appointment_data['id_users_provider']); - $customer_data = $this->Customers_Model - ->get_row($appointment_data['id_users_customer']); - } else { - // The customer is going to book an appointment so there is no - // need for the manage functionality to be initialized. - $manage_mode = FALSE; - $appointment_data = array(); - $provider_data = array(); - $customer_data = array(); - } + if (count($appointments_results) === 0) { + // The requested appointment doesn't exist in the database. Display + // a message to the customer. + $view_data = array( + 'message_title' => 'Appointment Not Found!', + 'message_text' => 'The appointment you requested does not exist in ' + . 'the system database anymore.', + 'message_icon' => $this->config->item('base_url') + . 'assets/images/error.png' + ); + $this->load->view('appointments/message', $view_data); + return; + } - // Load the book appointment view. - $view_data = array ( - 'available_services' => $available_services, - 'available_providers' => $available_providers, - 'company_name' => $company_name, - 'manage_mode' => $manage_mode, - 'appointment_data' => $appointment_data, - 'provider_data' => $provider_data, - 'customer_data' => $customer_data - ); + $appointment_data = $appointments_results[0]; + + $provider_data = $this->Providers_Model + ->get_row($appointment_data['id_users_provider']); + $customer_data = $this->Customers_Model + ->get_row($appointment_data['id_users_customer']); + + } else { + // The customer is going to book a new appointment so there is no + // need for the manage functionality to be initialized. + $manage_mode = FALSE; + $appointment_data = array(); + $provider_data = array(); + $customer_data = array(); + } + + // Load the book appointment view. + $view_data = array ( + 'available_services' => $available_services, + 'available_providers' => $available_providers, + 'company_name' => $company_name, + 'manage_mode' => $manage_mode, + 'appointment_data' => $appointment_data, + 'provider_data' => $provider_data, + 'customer_data' => $customer_data + ); + + } catch(Exception $exc) { + $view_data['exceptions'][] = $exc; + } $this->load->view('appointments/book', $view_data); @@ -79,6 +84,7 @@ class Appointments extends CI_Controller { // The page is a post-back. Register the appointment and send notification emails // to the provider and the customer that are related to the appointment. If google // sync is enabled then add the appointment to the provider's account. + try { $post_data = json_decode($_POST['post_data'], true); $appointment_data = $post_data['appointment']; @@ -104,66 +110,74 @@ class Appointments extends CI_Controller { // :: SYNCHRONIZE APPOINTMENT WITH PROVIDER'S GOOGLE CALENDAR // The provider must have previously granted access to his google calendar account // in order to sync the appointment. - $google_sync = $this->Providers_Model->get_setting('google_sync', - $appointment_data['id_users_provider']); + try { + $google_sync = $this->Providers_Model->get_setting('google_sync', + $appointment_data['id_users_provider']); - if ($google_sync == TRUE) { - $google_token = json_decode($this->Providers_Model - ->get_setting('google_token', $appointment_data['id_users_provider'])); - - $this->load->library('google_sync'); - $this->google_sync->refresh_token($google_token->refresh_token); + if ($google_sync == TRUE) { + $google_token = json_decode($this->Providers_Model + ->get_setting('google_token', $appointment_data['id_users_provider'])); - if ($post_data['manage_mode'] === FALSE) { - // Add appointment to Google Calendar. - $this->google_sync->add_appointment($appointment_data['id']); - } else { - // Update appointment to Google Calendar. - $appointment_data['id_google_calendar'] = - $this->Appointments_Model + $this->load->library('google_sync'); + $this->google_sync->refresh_token($google_token->refresh_token); + + if ($post_data['manage_mode'] === FALSE) { + // Add appointment to Google Calendar. + $this->google_sync->add_appointment($appointment_data, $provider_data, + $service_data, $customer_data, $company_settings); + } else { + // Update appointment to Google Calendar. + $appointment_data['id_google_calendar'] = $this->Appointments_Model ->get_value('id_google_calendar', $appointment_data['id']); - - $this->google_sync->update_appointment($appointment_data, $provider_data, - $service_data, $customer_data, $company_settings); - } - } + + $this->google_sync->update_appointment($appointment_data, $provider_data, + $service_data, $customer_data, $company_settings); + } + } + } catch(SyncException $syn_exc) { + $view_data['exceptions'][] = $syn_exc; + } // :: SEND NOTIFICATION EMAILS TO BOTH CUSTOMER AND PROVIDER - $this->load->library('Notifications'); - - if (!$post_data['manage_mode']) { - $customer_title = 'Your appointment has been successfully booked!'; - $customer_message = 'Thank you for arranging an appointment with us. ' - . 'Below you can see the appointment details. Make changes ' - . 'by clicking the appointment link.'; - $customer_link = $this->config->item('base_url') . 'appointments/index/' - . $appointment_data['hash']; - - $provider_title = 'A new appointment has been added to your plan.'; - $provider_message = 'You can make changes by clicking the appointment ' - . 'link below'; - $provider_link = $this->config->item('base_url') . 'backend/' - . $appointment_data['hash']; - } else { - $customer_title = 'Appointment changes have been successfully saved!'; - $customer_message = ''; - $customer_link = $this->config->item('base_url') . 'appointments/index/' - . $appointment_data['hash']; - - $provider_title = 'Appointment details have changed.'; - $provider_message = ''; - $provider_link = $this->config->item('base_url') . 'backend/' - . $appointment_data['hash']; + try { + $this->load->library('Notifications'); + + if (!$post_data['manage_mode']) { + $customer_title = 'Your appointment has been successfully booked!'; + $customer_message = 'Thank you for arranging an appointment with us. ' + . 'Below you can see the appointment details. Make changes ' + . 'by clicking the appointment link.'; + $customer_link = $this->config->item('base_url') . 'appointments/index/' + . $appointment_data['hash']; + + $provider_title = 'A new appointment has been added to your plan.'; + $provider_message = 'You can make changes by clicking the appointment ' + . 'link below'; + $provider_link = $this->config->item('base_url') . 'backend/' + . $appointment_data['hash']; + } else { + $customer_title = 'Appointment changes have been successfully saved!'; + $customer_message = ''; + $customer_link = $this->config->item('base_url') . 'appointments/index/' + . $appointment_data['hash']; + + $provider_title = 'Appointment details have changed.'; + $provider_message = ''; + $provider_link = $this->config->item('base_url') . 'backend/' + . $appointment_data['hash']; + } + + $this->notifications->send_appointment_details($appointment_data, $provider_data, + $service_data, $customer_data,$company_settings, $customer_title, + $customer_message, $customer_link, $customer_data['email']); + + $this->notifications->send_appointment_details($appointment_data, $provider_data, + $service_data, $customer_data, $company_settings, $provider_title, + $provider_message, $provider_link, $provider_data['email']); + } catch(NotificationException $not_exc) { + $view_data['exceptions'][] = $not_exc; } - - $this->notifications->send_appointment_details($appointment_data, $provider_data, - $service_data, $customer_data,$company_settings, $customer_title, - $customer_message, $customer_link, $customer_data['email']); - - $this->notifications->send_appointment_details($appointment_data, $provider_data, - $service_data, $customer_data, $company_settings, $provider_title, - $provider_message, $provider_link, $provider_data['email']); - + // :: LOAD THE BOOK SUCCESS VIEW $view_data = array( 'appointment_data' => $appointment_data, @@ -173,10 +187,7 @@ class Appointments extends CI_Controller { ); } catch(Exception $exc) { - $view_data['error'] = array( - 'message' => $exc->getMessage(), - 'technical' => $exc->getTraceAsString() - ); + $view_data['exceptions'][] = $exc; } $this->load->view('appointments/book_success', $view_data); @@ -257,14 +268,15 @@ class Appointments extends CI_Controller { /** * [AJAX] Get the available appointment hours for the given date. * - * This method answers to an AJAX request. It calculates the available hours for the - * given service, provider and date. + * This method answers to an AJAX request. It calculates the available hours + * for thegiven service, provider and date. * * @param numeric $_POST['service_id'] The selected service's record id. * @param numeric $_POST['provider_id'] The selected provider's record id. - * @param string $_POST['selected_date'] The selected date of which the available hours - * we want to see. - * @param numeric $_POST['service_duration'] The selected service duration in minutes. + * @param string $_POST['selected_date'] The selected date of which the + * available hours we want to see. + * @param numeric $_POST['service_duration'] The selected service duration in + * minutes. * @return Returns a json object with the available hours. */ public function ajax_get_available_hours() { @@ -272,78 +284,86 @@ class Appointments extends CI_Controller { $this->load->model('Appointments_Model'); $this->load->model('Settings_Model'); - // If manage mode is TRUE then the following we should not consider the selected - // appointment when calculating the available time periods of the provider. - $exclude_appointments = ($_POST['manage_mode'] === 'true') - ? array($_POST['appointment_id']) - : array(); - - $empty_periods = $this->get_provider_available_time_periods($_POST['provider_id'], - $_POST['selected_date'], $exclude_appointments); - - // Calculate the available appointment hours for the given date. - // The empty spaces are broken down to 15 min and if the service - // fit in each quarter then a new available hour is added to the - // $available hours array. - $available_hours = array(); - - foreach($empty_periods as $period) { - $start_hour = new DateTime($_POST['selected_date'] . ' ' . $period['start']); - $end_hour = new DateTime($_POST['selected_date'] . ' ' . $period['end']); - - $minutes = $start_hour->format('i'); - - if ($minutes % 15 != 0) { - // Change the start hour of the current space in order to be - // on of the following: 00, 15, 30, 45. - if ($minutes < 15) { - $start_hour->setTime($start_hour->format('H'), 15); - } else if ($minutes < 30) { - $start_hour->setTime($start_hour->format('H'), 30); - } else if ($minutes < 45) { - $start_hour->setTime($start_hour->format('H'), 45); - } else { - $start_hour->setTime($start_hour->format('H') + 1, 00); + try { + // If manage mode is TRUE then the following we should not consider the selected + // appointment when calculating the available time periods of the provider. + $exclude_appointments = ($_POST['manage_mode'] === 'true') + ? array($_POST['appointment_id']) + : array(); + + $empty_periods = $this->get_provider_available_time_periods($_POST['provider_id'], + $_POST['selected_date'], $exclude_appointments); + + // Calculate the available appointment hours for the given date. + // The empty spaces are broken down to 15 min and if the service + // fit in each quarter then a new available hour is added to the + // $available hours array. + + $available_hours = array(); + + foreach($empty_periods as $period) { + $start_hour = new DateTime($_POST['selected_date'] . ' ' . $period['start']); + $end_hour = new DateTime($_POST['selected_date'] . ' ' . $period['end']); + + $minutes = $start_hour->format('i'); + + if ($minutes % 15 != 0) { + // Change the start hour of the current space in order to be + // on of the following: 00, 15, 30, 45. + if ($minutes < 15) { + $start_hour->setTime($start_hour->format('H'), 15); + } else if ($minutes < 30) { + $start_hour->setTime($start_hour->format('H'), 30); + } else if ($minutes < 45) { + $start_hour->setTime($start_hour->format('H'), 45); + } else { + $start_hour->setTime($start_hour->format('H') + 1, 00); + } } - } - - $curr_hour = $start_hour; - $diff = $curr_hour->diff($end_hour); - - while(($diff->h * 60 + $diff->i) > intval($_POST['service_duration'])) { - $available_hours[] = $curr_hour->format('H:i'); - $curr_hour->add(new DateInterval("PT15M")); + + $curr_hour = $start_hour; $diff = $curr_hour->diff($end_hour); - } - } - - // If the selected date is today, remove past hours. It is important - // include the timeout before booking that is set in the backoffice - // the system. Normally we might want the customer to book an appointment - // that is at least half or one hour from now. The setting is stored in - // minutes. - if (date('m/d/Y', strtotime($_POST['selected_date'])) == date('m/d/Y')) { - if ($_POST['manage_mode'] === 'true') { - $book_advance_timeout = 0; - } else { - $book_advance_timeout = $this->Settings_Model - ->get_setting('book_advance_timeout'); - } - - foreach($available_hours as $index=>$value) { - $available_hour = strtotime($value); - $current_hour = strtotime('+' . $book_advance_timeout - . ' minutes', strtotime('now')); - - if ($available_hour <= $current_hour) { - unset($available_hours[$index]); + + while(($diff->h * 60 + $diff->i) > intval($_POST['service_duration'])) { + $available_hours[] = $curr_hour->format('H:i'); + $curr_hour->add(new DateInterval("PT15M")); + $diff = $curr_hour->diff($end_hour); } } + + // If the selected date is today, remove past hours. It is important + // include the timeout before booking that is set in the backoffice + // the system. Normally we might want the customer to book an appointment + // that is at least half or one hour from now. The setting is stored in + // minutes. + if (date('m/d/Y', strtotime($_POST['selected_date'])) == date('m/d/Y')) { + if ($_POST['manage_mode'] === 'true') { + $book_advance_timeout = 0; + } else { + $book_advance_timeout = $this->Settings_Model->get_setting( + 'book_advance_timeout'); + } + + foreach($available_hours as $index=>$value) { + $available_hour = strtotime($value); + $current_hour = strtotime('+' . $book_advance_timeout . ' minutes', + strtotime('now')); + + if ($available_hour <= $current_hour) { + unset($available_hours[$index]); + } + } + } + + $available_hours = array_values($available_hours); + + echo json_encode($available_hours); + + } catch(Exception $exc) { + echo json_encode(array( + 'exceptions' => array( exceptionToJavascript($exc) ) + )); } - - $available_hours = array_values($available_hours); - - echo json_encode($available_hours); } /** @@ -392,7 +412,7 @@ class Appointments extends CI_Controller { } catch(Exception $exc) { echo json_encode(array( - 'error' => $exc->getMessage() + 'exceptions' => array( exceptionToJavascript($exc) ) )); } } diff --git a/src/application/helpers/custom_exceptions_helper.php b/src/application/helpers/custom_exceptions_helper.php index c1a01c25..b5fdefdd 100644 --- a/src/application/helpers/custom_exceptions_helper.php +++ b/src/application/helpers/custom_exceptions_helper.php @@ -1,4 +1,4 @@ - +
+ +
+
+
' . $exception->getTraceAsString() . '
+
+
+
+ '; +} + +/** + * Get a javascript object of a given exception. + * + * This method is pretty useful whenever we need to pass an exception object + * to javascript during ajax calls. + * + * @param Exception $exception The given exception object. + * @return string Returns the json encoded object of the exception. + */ +function exceptionToJavascript($exception) { + return json_encode(array( + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'message' => $exception->getMessage(), + 'previous' => $exception->getPrevious(), + 'trace' => $exception->getTraceAsString() + )); +} + /* End of file exception_types_helper.php */ /* Location: ./application/helpers/exception_types_helper.php */ \ No newline at end of file diff --git a/src/application/views/appointments/book.php b/src/application/views/appointments/book.php index f9fdc0eb..02fd266b 100644 --- a/src/application/views/appointments/book.php +++ b/src/application/views/appointments/book.php @@ -136,6 +136,19 @@ } ?> + '; + echo '

Unexpected Errors

'; + foreach($exceptions as $exception) { + echo exceptionToHtml($exception); + } + echo ''; + } + ?> -

An Unexpected Error Occured

-
-
- -
-
-
' . $error['technical'] . '
-
-
-
-
'; + // Display exceptions (if any). + if (isset($exceptions)) { + echo '
'; + echo '

Unexpected Errors

'; + foreach($exceptions as $exception) { + echo exceptionToHtml($exception); + } + echo '
'; } ?> diff --git a/src/assets/js/frontend_book.js b/src/assets/js/frontend_book.js index 86fb068c..82e35a62 100644 --- a/src/assets/js/frontend_book.js +++ b/src/assets/js/frontend_book.js @@ -21,7 +21,7 @@ var FrontendBook = { * @param {bool} manageMode (OPTIONAL) Determines whether the customer is going * to make changes to an existing appointment rather than booking a new one. */ - initialize : function(bindEventHandlers, manageMode) { + initialize: function(bindEventHandlers, manageMode) { if (bindEventHandlers === undefined) { bindEventHandlers = true; // Default Value } @@ -44,10 +44,10 @@ var FrontendBook = { }); $('#select-date').datepicker({ - dateFormat : 'dd-mm-yy', - minDate : 0, - defaultDate : Date.today(), - onSelect : function(dateText, instance) { + dateFormat: 'dd-mm-yy', + minDate: 0, + defaultDate: Date.today(), + onSelect: function(dateText, instance) { FrontendBook.getAvailableHours(dateText); FrontendBook.updateConfirmFrame(); } @@ -73,9 +73,9 @@ var FrontendBook = { * This method binds the necessary event handlers for the book * appointments page. */ - bindEventHandlers : function() { + bindEventHandlers: function() { /** - * Event : Selected Provider "Changed" + * Event: Selected Provider "Changed" * * Whenever the provider changes the available appointment * date - time periods must be updated. @@ -86,7 +86,7 @@ var FrontendBook = { }); /** - * Event : Selected Service "Changed" + * Event: Selected Service "Changed" * * When the user clicks on a service, its available providers should * become visible. @@ -113,7 +113,7 @@ var FrontendBook = { }); /** - * Event : Next Step Button "Clicked" + * Event: Next Step Button "Clicked" * * This handler is triggered every time the user pressed the * "next" button on the book wizard. Some special tasks might @@ -155,7 +155,7 @@ var FrontendBook = { }); /** - * Event : Back Step Button "Clicked" + * Event: Back Step Button "Clicked" * * This handler is triggered every time the user pressed the * "back" button on the book wizard. @@ -171,7 +171,7 @@ var FrontendBook = { }); /** - * Event : Available Hour "Click" + * Event: Available Hour "Click" * * Triggered whenever the user clicks on an available hour * for his appointment. @@ -195,10 +195,10 @@ var FrontendBook = { event.preventDefault(); var dialogButtons = { - 'Yes' : function() { + 'Yes': function() { $('#cancel-appointment-form').submit(); }, - 'No' : function() { + 'No': function() { $('#message_box').dialog('close'); } }; @@ -210,7 +210,7 @@ var FrontendBook = { } /** - * Event : Book Appointment Form "Submit" + * Event: Book Appointment Form "Submit" * * Before the form is submitted to the server we need to make sure that * in the meantime the selected appointment date/time wasn't reserved by @@ -219,37 +219,37 @@ var FrontendBook = { * @task Fix the problem with this event handler. Book does not work anymore. */ $('#book-appointment-form').submit(function() { - event.preventDefault(); - - var formData = jQuery.parseJSON($('input[name="post_data"]').val()); - - var postData = { - 'id_users_provider' : formData['appointment']['id_users_provider'], - 'id_services' : formData['appointment']['id_services'], - 'start_datetime' : formData['appointment']['start_datetime'] - }; - - var postUrl = GlobalVariables.baseUrl + 'appointments/ajax_check_datetime_availability'; - - $.post(postUrl, postData, function(response) { - //////////////////////////////////////////////////////////////////////// - console.log('Check Date/Time Availability Post Response :', response); - //////////////////////////////////////////////////////////////////////// - - if (response.error) { - GeneralFunctions.displayMessageBox('An Unexpected Error Occured', - response.error); - } - - if (response === true) { - $('#book-appointment-form').submit(); - } else { - GeneralFunctions.displayMessageBox('Appointment Hour Taken', 'Unfortunately ' - + 'the selected appointment hour is not available anymore. Please select ' - + 'another hour.'); - FrontendBook.getAvailableHours($('#select-date').val()); - } - }, 'json'); +// event.preventDefault(); +// +// var formData = jQuery.parseJSON($('input[name="post_data"]').val()); +// +// var postData = { +// 'id_users_provider' : formData['appointment']['id_users_provider'], +// 'id_services' : formData['appointment']['id_services'], +// 'start_datetime' : formData['appointment']['start_datetime'] +// }; +// +// var postUrl = GlobalVariables.baseUrl + 'appointments/ajax_check_datetime_availability'; +// +// $.post(postUrl, postData, function(response) { +// //////////////////////////////////////////////////////////////////////// +// console.log('Check Date/Time Availability Post Response :', response); +// //////////////////////////////////////////////////////////////////////// +// +// if (response.error) { +// GeneralFunctions.displayMessageBox('An Unexpected Error Occured', +// response.error); +// } +// +// if (response === true) { +// $('#book-appointment-form').submit(); +// } else { +// GeneralFunctions.displayMessageBox('Appointment Hour Taken', 'Unfortunately ' +// + 'the selected appointment hour is not available anymore. Please select ' +// + 'another hour.'); +// FrontendBook.getAvailableHours($('#select-date').val()); +// } +// }, 'json'); }); }, @@ -260,7 +260,7 @@ var FrontendBook = { * @param {string} selDate The selected date of which the available * hours we need to receive. */ - getAvailableHours : function(selDate) { + getAvailableHours: function(selDate) { // Find the selected service duration (it is going to // be send within the "postData" object. var selServiceDuration = 15; // Default value of duration (in minutes). @@ -276,70 +276,69 @@ var FrontendBook = { ? GlobalVariables.appointmentData['id'] : undefined; var postData = { - 'service_id' : $('#select-service').val(), - 'provider_id' : $('#select-provider').val(), - 'selected_date' : selDate, - 'service_duration' : selServiceDuration, - 'manage_mode' : FrontendBook.manageMode, - 'appointment_id' : appointmentId - }; + 'service_id': $('#select-service').val(), + 'provider_id': $('#select-provider').val(), + 'selected_date': selDate, + 'service_duration': selServiceDuration, + 'manage_mode': FrontendBook.manageMode, + 'appointment_id': appointmentId + } // Make ajax post request and get the available hours. var ajaxurl = GlobalVariables.baseUrl + 'appointments/ajax_get_available_hours'; - jQuery.post(ajaxurl, postData, function(postResponse) { - //////////////////////////////////////////////////////////////////////////////// - //console.log('\n\n Get Available Hours Post Response :', postResponse, '\n\n'); - //////////////////////////////////////////////////////////////////////////////// + jQuery.post(ajaxurl, postData, function(response) { + /////////////////////////////////////////////////////////////// + //console.log('Get Available Hours JSON Response :', response); + /////////////////////////////////////////////////////////////// - try { - var jsonResponse = jQuery.parseJSON(postResponse); - //////////////////////////////////////////////////////////////////////////////// - //console.log('\n\n Get Available Hours JSON Response :', jsonResponse, '\n\n'); - //////////////////////////////////////////////////////////////////////////////// - - if (jsonResponse.length > 0) { - // Fill the available time div - var currColumn = 1; - $('#available-hours').html('
'); - - $.each(jsonResponse, function(index, availableHour) { - if ((currColumn * 10) < (index + 1)) { - currColumn++; - $('#available-hours') - .append('
'); - } - - $('#available-hours div:eq(' + (currColumn - 1) + ')') - .append('' + availableHour - + '
'); - }); - - if (FrontendBook.manageMode) { - // Set the appointment start time as selected. - $('.available-hour').removeClass('selected-hour'); - $('.available-hour').filter(function() { - return $(this).text() === Date.parseExact( - GlobalVariables.appointmentData['start_datetime'], - 'yyyy-MM-dd HH:mm:ss').toString('HH:mm'); - }).addClass('selected-hour'); - } else { - // Set the first item as selected. - $('.available-hour:eq(0)').addClass('selected-hour'); - } - - FrontendBook.updateConfirmFrame(); - } else { - $('#available-hours').text('There are no available appointment' - + 'hours for the selected date. Please choose another ' - + 'date.'); - } - - } catch(exception) { + if (response.exceptions) { + // Display a friendly message to the user with the exceptions information. + response.exceptions = GeneralFunctions.parseExceptions(response.exceptions); GeneralFunctions.displayMessageBox('Unexpected Error', 'An unexpected ' + 'error occured during the available hours calculation. Please ' + 'refresh the page and try again.'); + $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions)); + console.log('Get Available Hours Exceptions:', response.exceptions); + return; } - }); + + // The response contains the available hours for the selected provider and + // service. Fill the available hours div with response data. + if (response.length > 0) { + + var currColumn = 1; + $('#available-hours').html('
'); + + $.each(response, function(index, availableHour) { + if ((currColumn * 10) < (index + 1)) { + currColumn++; + $('#available-hours').append('
'); + } + + $('#available-hours div:eq(' + (currColumn - 1) + ')').append( + '' + availableHour + '
'); + }); + + if (FrontendBook.manageMode) { + // Set the appointment's start time as the default selection. + $('.available-hour').removeClass('selected-hour'); + $('.available-hour').filter(function() { + return $(this).text() === Date.parseExact( + GlobalVariables.appointmentData['start_datetime'], + 'yyyy-MM-dd HH:mm:ss').toString('HH:mm'); + }).addClass('selected-hour'); + } else { + // Set the first available hour as the default selection. + $('.available-hour:eq(0)').addClass('selected-hour'); + } + + FrontendBook.updateConfirmFrame(); + + } else { + $('#available-hours').text('There are no available appointment' + + 'hours for the selected date. Please choose another date.'); + } + }, 'json'); }, /** @@ -348,7 +347,7 @@ var FrontendBook = { * * @return {bool} Returns the validation result. */ - validateCustomerForm : function() { + validateCustomerForm: function() { $('#wizard-frame-3 input').css('border', ''); try { @@ -382,7 +381,7 @@ var FrontendBook = { * page with the latest customer settigns and input for the appointment * booking. */ - updateConfirmFrame : function() { + updateConfirmFrame: function() { // :: UPDATE APPOINTMENT DETAILS DIV var selectedDate = $('#select-date').datepicker('getDate'); if (selectedDate !== null) { @@ -420,22 +419,22 @@ var FrontendBook = { var postData = new Object(); postData['customer'] = { - 'last_name' : $('#last-name').val(), - 'first_name' : $('#first-name').val(), - 'email' : $('#email').val(), - 'phone_number' : $('#phone-number').val(), - 'address' : $('#address').val(), - 'city' : $('#city').val(), - 'zip_code' : $('#zip-code').val() + 'last_name': $('#last-name').val(), + 'first_name': $('#first-name').val(), + 'email': $('#email').val(), + 'phone_number': $('#phone-number').val(), + 'address': $('#address').val(), + 'city': $('#city').val(), + 'zip_code': $('#zip-code').val() }; postData['appointment'] = { - 'start_datetime' : $('#select-date').datepicker('getDate').toString('yyyy-MM-dd') - + ' ' + $('.selected-hour').text() + ':00', - 'end_datetime' : FrontendBook.calcEndDatetime(), - 'notes' : $('#notes').val(), - 'id_users_provider' : $('#select-provider').val(), - 'id_services' : $('#select-service').val() + 'start_datetime': $('#select-date').datepicker('getDate').toString('yyyy-MM-dd') + + ' ' + $('.selected-hour').text() + ':00', + 'end_datetime': FrontendBook.calcEndDatetime(), + 'notes': $('#notes').val(), + 'id_users_provider': $('#select-provider').val(), + 'id_services': $('#select-service').val() }; postData['manage_mode'] = FrontendBook.manageMode; @@ -454,7 +453,7 @@ var FrontendBook = { * * @return {string} Returns the end datetime in string format. */ - calcEndDatetime : function() { + calcEndDatetime: function() { // Find selected service duration. var selServiceDuration = undefined; @@ -489,7 +488,7 @@ var FrontendBook = { * @param {object} customerData Selected customer's data. * @returns {bool} Returns the operation result. */ - applyAppointmentData : function(appointmentData, providerData, customerData) { + applyAppointmentData: function(appointmentData, providerData, customerData) { try { // Select Service & Provider $('#select-service').val(appointmentData['id_services']).trigger('change'); @@ -505,7 +504,6 @@ var FrontendBook = { $('#first-name').val(customerData['first_name']); $('#email').val(customerData['email']); $('#phone-number').val(customerData['phone_number']); - $('#address').val(customerData['address']); $('#city').val(customerData['city']); $('#zip-code').val(customerData['zip_code']); @@ -516,7 +514,7 @@ var FrontendBook = { return true; } catch(exc) { - console.log(exc); + console.log(exc); // log exception return false; } } diff --git a/src/assets/js/general_functions.js b/src/assets/js/general_functions.js index 2d51fa64..1a711742 100644 --- a/src/assets/js/general_functions.js +++ b/src/assets/js/general_functions.js @@ -175,5 +175,54 @@ var GeneralFunctions = { validateEmail: function(email) { var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; return reg.test(email); + }, + + /** + * This method returns the exception html display for javascript ajax calls. + * It uses the Bootstrap collapse module to show exception messages when the + * user opens the "Details" collapse component. + * + * @param {array} exceptions Contains the exceptions to be displayed. + * @returns {String} Returns the html markup for the exceptions. + */ + exceptionsToHtml: function(exceptions) { + var html = + '
' + + '
' + + '
' + + '' + + 'Details' + + '' + + '
'; + + $.each(exceptions, function(index, exception) { + html += + '
' + + '
' + + '
' + exception['message'] + '
' + + '
' + + '
'; + }); + + html += '
'; + + return html; + }, + + /** + * This method parse the json encoded strings that are fetched by ajax calls. + * + * @param {array} exceptions Exception array returned by an ajax call. + * @returns {Array} Returns the parsed js objects. + */ + parseExceptions: function(exceptions) { + var parsedExceptions = new Array(); + + $.each(exceptions, function(index, exception) { + parsedExceptions.push(jQuery.parseJSON(exception)); + }); + + return parsedExceptions; } }; \ No newline at end of file