Added user friendly display of exceptions, raised on php (need to apply this methodology to the rest of the code).

This commit is contained in:
alextselegidis@gmail.com 2013-07-02 17:18:19 +00:00
parent 6050bf75c3
commit bd6cab36f0
6 changed files with 440 additions and 323 deletions

View file

@ -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) )
));
}
}

View file

@ -1,4 +1,4 @@
<?php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Database Exception Class
@ -20,5 +20,54 @@ class NotificationException extends Exception {}
*/
class SyncException extends Exception {}
/**
* Print an exception to an HTML text.
*
* This method is used to display exceptions in a way that is userful and easy
* for the user to see. It uses the Bootstrap CSS accordion markup to display
* the message and when the user clicks on it the exception trace will be revealed.
*
* @param Exception $exception The exception to be displayed.
* @return string Returns the html markup of the exception.
*/
function exceptionToHtml($exception) {
return
'<div class="accordion" id="error-accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse"
data-parent="#error-accordion" href="#error-technical">' .
$exception->getMessage() . '
</a>
</div>
<div id="error-technical" class="accordion-body collapse">
<div class="accordion-inner">
<pre>' . $exception->getTraceAsString() . '</pre>
</div>
</div>
</div>
</div>';
}
/**
* 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 */

View file

@ -136,6 +136,19 @@
}
?>
<?php
// ------------------------------------------------------
// DISPLAY EXCEPTIONS (IF ANY)
// ------------------------------------------------------
if (isset($exceptions)) {
echo '<div style="margin: 10px">';
echo '<h4>Unexpected Errors</h4>';
foreach($exceptions as $exception) {
echo exceptionToHtml($exception);
}
echo '</div>';
}
?>
<?php
// ------------------------------------------------------
// SELECT SERVICE AND PROVIDER

View file

@ -192,26 +192,14 @@
</button>
<?php
// Display error message (if any).
if (isset($error)) {
echo '
<hr>
<h4>An Unexpected Error Occured</h4>
<div class="accordion" id="error-accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse"
data-parent="#error-accordion" href="#error-technical">' .
$error['message'] . '
</a>
</div>
<div id="error-technical" class="accordion-body collapse">
<div class="accordion-inner">
<pre>' . $error['technical'] . '</pre>
</div>
</div>
</div>
</div>';
// Display exceptions (if any).
if (isset($exceptions)) {
echo '<div style="margin: 10px">';
echo '<h4>Unexpected Errors</h4>';
foreach($exceptions as $exception) {
echo exceptionToHtml($exception);
}
echo '</div>';
}
?>
</div>

View file

@ -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('<div style="width:50px; float:left;"></div>');
$.each(jsonResponse, function(index, availableHour) {
if ((currColumn * 10) < (index + 1)) {
currColumn++;
$('#available-hours')
.append('<div style="width:50px; float:left;"></div>');
}
$('#available-hours div:eq(' + (currColumn - 1) + ')')
.append('<span class="available-hour">' + availableHour
+ '</span><br/>');
});
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('<div style="width:50px; float:left;"></div>');
$.each(response, function(index, availableHour) {
if ((currColumn * 10) < (index + 1)) {
currColumn++;
$('#available-hours').append('<div style="width:50px; float:left;"></div>');
}
$('#available-hours div:eq(' + (currColumn - 1) + ')').append(
'<span class="available-hour">' + availableHour + '</span><br/>');
});
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;
}
}

View file

@ -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 =
'<div class="accordion" id="error-accordion">' +
'<div class="accordion-group">' +
'<div class="accordion-heading">' +
'<a class="accordion-toggle" data-toggle="collapse" ' +
'data-parent="#error-accordion" href="#error-technical">' +
'Details' +
'</a>' +
'</div>';
$.each(exceptions, function(index, exception) {
html +=
'<div id="error-technical" class="accordion-body collapse">' +
'<div class="accordion-inner">' +
'<pre>' + exception['message'] + '</pre>' +
'</div>' +
'</div>';
});
html += '</div></div>';
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;
}
};