diff --git a/src/application/views/emails/book_success.php b/src/application/views/emails/book_success.php
new file mode 100644
index 00000000..5f03113d
--- /dev/null
+++ b/src/application/views/emails/book_success.php
@@ -0,0 +1,56 @@
+
+
+
Appointment Book Success
+
+
+
+
+
+
+
Your appointment has been successfully booked!
+
+ Thank you $customer_name for arranging an appointment with us.
+ Below you can see the appointment details. Click on the edit
+ link to make changes to your appointment.
+
+
+
Appointment Details
+
+
+ Service
+ $appointment_service
+
+
+ Provider
+ $appointment_provider
+
+
+ Date
+ $appointment_date
+
+
+ Duration
+ $appointment_duration
+
+
+
+
Edit Link
+
+ Press the following link to make changes to your appointment reservation.
+ You are able to change the appointment details three hours before
+ the appointment.
+
+
$appointment_link
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/application/views/emails/new_appointment.php b/src/application/views/emails/new_appointment.php
new file mode 100644
index 00000000..5c5e4db2
--- /dev/null
+++ b/src/application/views/emails/new_appointment.php
@@ -0,0 +1,67 @@
+
+
+
New Appointment
+
+
+
+
+
+
+
+
A new appointment has been added to your plan.
+
Appointment Details
+
+
+ Service
+ $appointment_service
+
+
+ Provider
+ $appointment_provider
+
+
+ Date
+ $appointment_date
+
+
+ Duration
+ $appointment_duration
+
+
+
+
Customer Details
+
+
+ Name
+ $customer_name
+
+
+ Email
+ $customer_email
+
+
+ Phone
+ $customer_phone
+
+
+ Address
+ $customer_address
+
+
+
+
Appointment Link
+
You can make more actions by pressing the following link.
+
$appointment_link
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/css/style.css b/src/assets/css/style.css
index 9ebad032..9acea715 100644
--- a/src/assets/css/style.css
+++ b/src/assets/css/style.css
@@ -10,23 +10,23 @@ body {
}
-/* REGISTER APPOINTMENT
+/* BOOK APPOINTMENT WIZARD
------------------------------------------------------------------------------ */
-#book-appointment {
+#book-appointment-wizard {
width: 660px;
background: #FFF;
box-shadow: 0px 1px 1px #B6B6B6;
min-height: 480px;
}
-#book-appointment #top-bar {
+#book-appointment-wizard #header {
padding: 5px;
height: 70px;
background: #3DD481;
border-bottom: 4px solid #1A865F;
}
-#book-appointment #business-name {
+#book-appointment-wizard #company-name {
font-weight: bold;
color: #FFF;
font-size: 27px;
@@ -36,43 +36,43 @@ body {
float: left;
}
-#book-appointment #book-steps {
+#book-appointment-wizard #steps {
width: 204px;
display: inline-block;
float: right;
margin-top: 15px;
}
-#book-appointment .book-appoinment-step {
+#book-appointment-wizard .wizard-frame {
padding: 10px 20px;
height: 434px;
}
-#book-appointment .book-appoinment-step .step-frame {
+#book-appointment-wizard .wizard-frame .frame-container {
height: 370px;
margin-bottom: 10px;
}
-#book-appointment .step-frame .step-title {
+#book-appointment-wizard .frame-container .frame-title {
text-align: center;
margin-bottom: 28px;
}
-#book-appointment .step-frame .step-content {
+#book-appointment-wizard .frame-container .frame-content {
margin: auto;
float: none;
}
-#book-appointment .command-buttons {
+#book-appointment-wizard .wizard-frame .command-buttons {
float: right;
}
-#book-appointment .command-buttons .btn {
+#book-appointment-wizard .wizard-frame .command-buttons .btn {
min-width: 80px;
margin-right: 10px;
}
-#book-appointment .book-step {
+#book-appointment-wizard .book-step {
display: inline-block;
height: 20px;
width: 20px;
@@ -84,14 +84,14 @@ body {
border: 3px solid #38A07A;
}
-#book-appointment .book-step strong {
+#book-appointment-wizard .book-step strong {
font-size: 18px;
display: block;
text-align: center;
color: #B6DFC6;
}
-#book-appointment .active-step {
+#book-appointment-wizard .active-step {
display: inline-block;
height: 20px;
width: 20px;
@@ -103,40 +103,49 @@ body {
border: 3px solid #38A07A;
}
-#book-appointment .active-step strong {
+#book-appointment-wizard .active-step strong {
color: #396946;
font-size: 25px;
}
-#book-appointment #frame-footer {
+#book-appointment-wizard #frame-footer {
padding: 10px;
text-align: center;
border-top: 1px solid #EEE;
background: #FAFAFA;
}
-.custom-qtip {
+#book-appointment-wizard #steps .custom-qtip {
border-width: 2px;
}
-#available-hours .available-hour {
+#book-appointment-wizard #available-hours .available-hour {
font-size: 15px;
padding: 1px;
display: inline-block;
}
-#available-hours .available-hour:hover {
+#book-appointment-wizard #available-hours .available-hour:hover {
font-weight: bold;
background-color: #CAEDF3;
cursor: pointer;
}
-#available-hours .selected-hour {
+#book-appointment-wizard #available-hours .selected-hour {
color: #0088cc;
font-weight: bold;
text-decoration: underline;
}
-#book-appointment .span3 {
+#book-appointment-wizard .span3 {
min-width: 270px; /* This is especially needed for ie8 */
}
+
+#cancel-appointment-frame {
+
+}
+
+
+
+
+
diff --git a/src/assets/js/frontend/book_appointment.js b/src/assets/js/frontend/book_appointment.js
index 7bf770e0..57d6c73f 100644
--- a/src/assets/js/frontend/book_appointment.js
+++ b/src/assets/js/frontend/book_appointment.js
@@ -1,20 +1,34 @@
/**
- * This class implements the book appointment page functionality.
- * Once the initialize() method is called the page is fully functional
- * and can serve the appointment booking process.
+ * This class implements the book appointment page functionality. Once
+ * the initialize() method is called the page is fully functional and
+ * can serve the appointment booking process.
*
- * @class Implelements the js part of the appointment booking page.
+ * @class Implements the js part of the appointment booking page.
*/
var bookAppointment = {
+ /**
+ * Determines the functionality of the page.
+ *
+ * @type Boolean
+ */
+ manageMode : false,
+
/**
* This method initializes the book appointment page.
*
- * @param {bool} bindEventHandlers (OPTIONAL) Determines wether
+ * @param {bool} bindEventHandlers (OPTIONAL) Determines whether
* the default event handlers will be binded to the dom elements.
+ * @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) {
- if (bindEventHandlers == undefined) {
- bindEventHandlers = true; // Default value
+ initialize : function(bindEventHandlers, manageMode) {
+ if (bindEventHandlers === undefined) {
+ bindEventHandlers = true; // Default Value
+ }
+
+ if (manageMode === undefined) {
+ bookAppointment.manageMode = false; // Default Value
}
// Initialize page's components (tooltips, datepickers etc).
@@ -34,7 +48,7 @@ var bookAppointment = {
defaultDate : Date.today(),
onSelect : function(dateText, instance) {
bookAppointment.getAvailableHours(dateText);
- bookAppointment.updateConfirmData();
+ bookAppointment.updateConfirmFrame();
}
});
@@ -43,22 +57,31 @@ var bookAppointment = {
if (bindEventHandlers) {
bookAppointment.bindEventHandlers();
}
-
- // Execute other necessary operations on startup.
- $('#select-service').trigger('change');
+
+ // If the manage mode is true, the appointments data should be
+ // loaded by default.
+ if (bookAppointment.manageMode) {
+ bookAppointment.applyAppointmentData(GlobalVariables.appointmentData,
+ GlobalVariables.providerData, GlobalVariables.customerData);
+ } else {
+ $('#select-service').trigger('change'); // Load the available hours.
+ }
},
/**
- * This method binds the necessary event handlers
- * for the book appointments page.
+ * This method binds the necessary event handlers for the book
+ * appointments page.
*/
bindEventHandlers : function() {
/**
* Event : Selected Provider "Changed"
+ *
+ * Whenever the provider changes the available appointment
+ * date - time periods must be updated.
*/
$('#select-provider').change(function() {
bookAppointment.getAvailableHours(Date.today().toString('dd-MM-yyyy'));
- bookAppointment.updateConfirmData();
+ bookAppointment.updateConfirmFrame();
});
/**
@@ -71,21 +94,21 @@ var bookAppointment = {
var currServiceId = $('#select-service').val();
$('#select-provider').empty();
- $.each(GlobalVariables.providers, function(indexProvider, provider) {
+ $.each(GlobalVariables.availableProviders, function(indexProvider, provider) {
$.each(provider['services'], function(indexService, serviceId) {
- // If the current provider is able to provide the selected
- // service, add him to the listbox.
+ // If the current provider is able to provide the selected service,
+ // add him to the listbox.
if (serviceId == currServiceId) {
var optionHtml = '
'
- + provider['last_name'] + ' ' + provider['first_name']
- + ' ';
+ + provider['last_name'] + ' ' + provider['first_name']
+ + '';
$('#select-provider').append(optionHtml);
}
});
});
bookAppointment.getAvailableHours(Date.today().toString('dd-MM-yyyy'));
- bookAppointment.updateConfirmData();
+ bookAppointment.updateConfirmFrame();
});
/**
@@ -101,9 +124,10 @@ var bookAppointment = {
if ($(this).attr('data-step_index') === '2') {
if ($('.selected-hour').length == 0) {
if ($('#select-hour-prompt').length == 0) {
- $('#available-hours').append('
Please select an appointment hour before '
- + 'continuing! ');
+ $('#available-hours').append('
'
+ + '
'
+ + 'Please select an appointment hour before continuing!'
+ + ' ');
}
return;
}
@@ -112,10 +136,10 @@ var bookAppointment = {
// If we are on the 3rd tab then we will need to validate the user's
// input before proceeding to the next step.
if ($(this).attr('data-step_index') === '3') {
- if (!bookAppointment.validateCustomerDataForm()) {
+ if (!bookAppointment.validateCustomerForm()) {
return; // Validation failed, do not continue.
} else {
- bookAppointment.updateConfirmData();
+ bookAppointment.updateConfirmFrame();
}
}
@@ -125,7 +149,7 @@ var bookAppointment = {
$(this).parents().eq(1).hide('fade', function() {
$('.active-step').removeClass('active-step');
$('#step-' + nextTabIndex).addClass('active-step');
- $('#book-appointment-' + nextTabIndex).show('fade');
+ $('#wizard-frame-' + nextTabIndex).show('fade');
});
});
@@ -141,7 +165,7 @@ var bookAppointment = {
$(this).parents().eq(1).hide('fade', function() {
$('.active-step').removeClass('active-step');
$('#step-' + prevTabIndex).addClass('active-step');
- $('#book-appointment-' + prevTabIndex).show('fade');
+ $('#wizard-frame-' + prevTabIndex).show('fade');
});
});
@@ -154,7 +178,7 @@ var bookAppointment = {
$('#available-hours').on('click', '.available-hour', function() {
$('.selected-hour').removeClass('selected-hour');
$(this).addClass('selected-hour');
- bookAppointment.updateConfirmData();
+ bookAppointment.updateConfirmFrame();
});
},
@@ -169,24 +193,31 @@ var bookAppointment = {
// Find the selected service duration (it is going to
// be send within the "postData" object.
var selServiceDuration = 15; // Default value of duration (in minutes).
- $.each(GlobalVariables.services, function(index, service) {
+ $.each(GlobalVariables.availableServices, function(index, service) {
if (service['id'] == $('#select-service').val()) {
selServiceDuration = service['duration'];
}
});
+
+ // If the manage mode is true then the appointment's start
+ // date should return as available too.
+ var appointmentId = (bookAppointment.manageMode)
+ ? GlobalVariables.appointmentData['id'] : undefined;
var postData = {
- 'service_id' : $('#select-service').val(),
- 'provider_id' : $('#select-provider').val(),
- 'selected_date' : selDate,
- 'service_duration' : selServiceDuration
+ 'service_id' : $('#select-service').val(),
+ 'provider_id' : $('#select-provider').val(),
+ 'selected_date' : selDate,
+ 'service_duration' : selServiceDuration,
+ 'manage_mode' : bookAppointment.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');
+ //console.log('\n\n Get Available Hours Post Response :', postResponse, '\n\n');
////////////////////////////////////////////////////////////////////////////////
try {
@@ -199,27 +230,32 @@ var bookAppointment = {
// 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')
+ .append('
');
}
$('#available-hours div:eq(' + (currColumn - 1) + ')')
- .append('
' + availableHour + ' ');
+ .append('
' + availableHour
+ + ' ');
});
// Set the first item as selected.
$('.available-hour:eq(0)').addClass('selected-hour');
- bookAppointment.updateConfirmData();
+ bookAppointment.updateConfirmFrame();
} else {
- $('#available-hours').text('There are not available appointment hours for '
- + 'the selected date. Please choose another date.');
+ $('#available-hours').text('There are no available appointment'
+ + 'hours for the selected date. Please choose another '
+ + 'date.');
}
} catch(exception) {
- GeneralFunctions.displayMessageBox('Unexpected Error', 'An unexpected error occured '
- + 'during the available hours calculation. Please refresh the page and try again.');
+ GeneralFunctions.displayMessageBox('Unexpected Error', 'An unexpected'
+ + 'error occured during the available hours calculation. Please'
+ + 'refresh the page and try again.');
}
});
},
@@ -230,7 +266,7 @@ var bookAppointment = {
*
* @return {bool} Returns the validation result.
*/
- validateCustomerDataForm : function() {
+ validateCustomerForm : function() {
var validationResult = true;
$('.required').css('border', '');
@@ -249,7 +285,7 @@ var bookAppointment = {
* page with the latest customer settigns and input for the appointment
* booking.
*/
- updateConfirmData : function() {
+ updateConfirmFrame : function() {
/*** SET APPOINTMENT INFO ***/
var selectedDate = $('#select-date').datepicker('getDate');
if (selectedDate !== null) {
@@ -308,7 +344,7 @@ var bookAppointment = {
// Find selected service duration.
var selServiceDuration = undefined;
- $.each(GlobalVariables.services, function(index, service) {
+ $.each(GlobalVariables.availableServices, function(index, service) {
if (service.id == $('#select-service').val()) {
selServiceDuration = service.duration;
return; // Stop searching ...
@@ -328,5 +364,47 @@ var bookAppointment = {
}
return endDatetime.toString('yyyy-MM-dd HH:mm:ss');
+ },
+
+ /**
+ * This method applies the appointment's data to the wizard so
+ * that the user can start making changes on an existing record.
+ *
+ * @param {object} appointmentData Selected appointment's data.
+ * @param {object} providerData Selected provider's data.
+ * @param {object} customerData Selected customer's data.
+ * @returns {bool} Returns the operation result.
+ */
+ applyAppointmentData : function(appointmentData, providerData, customerData) {
+ try {
+ // Select Service & Provider
+ $('#select-service').val(appointmentData['id_services']);
+ $('#select-provider').val(appointmentData['id_users_provider']);
+
+ // Set Appointment Date
+ $('.available-hour').removeClass('selected-hour');
+ $('.available-hour').filter(function() {
+ return $(this).text() === Date.parseExact(appointmentData['start_datetime'],
+ 'yyyy-MM-dd HH:mm').toString('HH:mm');
+ }).addClass('selected-hour');
+
+ // Apply Customer's Data
+ $('last-name').val(customerData['last_name']);
+ $('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']);
+ $('notes').text(customerData['notes']);
+
+ bookAppointment.updateConfirmFrame();
+
+ return true;
+ } catch(exc) {
+ console.log(exc);
+ return false;
+ }
}
}
\ No newline at end of file
diff --git a/src/assets/js/general_functions.js b/src/assets/js/general_functions.js
index 357ff292..93839c7d 100644
--- a/src/assets/js/general_functions.js
+++ b/src/assets/js/general_functions.js
@@ -82,4 +82,24 @@ GeneralFunctions.centerElementOnPage = function(elementHandle) {
});
});
$(window).resize();
+}
+
+/**
+ * This function retrieves a parameter from a "GET" formed url.
+ *
+ * @link http://www.netlobo.com/url_query_string_javascript.html
+ *
+ * @param {string} url The selected url.
+ * @param {string} name The parameter name.
+ * @returns {String} Returns the parameter value.
+ */
+GeneralFunctions.getUrlParameter = function(url, parameterName) {
+ parameterName = parameterName.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+ var regexS = "[\\#&]"+parameterName+"=([^]*)";
+ var regex = new RegExp( regexS );
+ var results = regex.exec( url );
+ if( results == null )
+ return "";
+ else
+ return results[1];
}
\ No newline at end of file