-
-
-
+
+
+
+
+
+
Customers
+
-
-
+
+
+
+
+
+
+
Details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Fields with * are required!
+
+
+
\ No newline at end of file
diff --git a/src/assets/css/backend.css b/src/assets/css/backend.css
index 5225d227..ed8d0617 100644
--- a/src/assets/css/backend.css
+++ b/src/assets/css/backend.css
@@ -55,6 +55,25 @@ root {
/* BACKEND CUSTOMERS PAGE
-------------------------------------------------------------------- */
+#customers-page #filter { margin: 15px 0px 15px 15px; }
+#customers-page #filter-results { overflow-y: auto; }
+#customers-page #filter-results .customer-row { padding: 10px 7px; border-radius: 3px; }
+#customers-page #filter-results .customer-row:hover { background-color: #C6E7D5; cursor: pointer; }
+#customers-page #filter-results hr { margin: 5px 0; }
+
+#customers-page #details { margin: 15px 0 15px 15px; }
+#customers-page #details .btn-toolbar { margin-top: 0px; }
+#customers-page #details div.span5 { margin-left: 0; }
+
+#customers-page #customer-appointments { height: 250px; border: 1px solid #CCC; border-radius: 3px;
+ margin-bottom: 20px; overflow-y: auto; }
+#customers-page #customer-appointments .appointment-row { padding: 7px; border-bottom: 1px solid #CCC; }
+#customers-page #customer-appointments .appointment-row:hover { background-color: #C6E7D5; cursor: pointer; }
+
+#customers-page #details input,
+#customers-page #details textarea { background-color: white; cursor: default; }
+
+#customers-page .selected-row { background-color: #EFFDF7; }
/* BACKEND SERVICES PAGE
diff --git a/src/assets/js/backend_customers.js b/src/assets/js/backend_customers.js
index 83ac8c18..99d0abf1 100644
--- a/src/assets/js/backend_customers.js
+++ b/src/assets/js/backend_customers.js
@@ -7,6 +7,10 @@
*/
var BackendCustomers = {
+ filterResults: {},
+ selectedCustomer: {},
+ selectedAppointment: {},
+
/**
* This method initializes the backend customers page. If you use this namespace
* in a different page do not use this method.
@@ -18,11 +22,10 @@ var BackendCustomers = {
if (bindDefaultEventHandlers === undefined) {
bindDefaultEventHandlers = false; // default value
}
-
- // :: INITIALIZE BACKEND CUSTOMERS PAGE
+
BackendCustomers.filterCustomers('');
-
- // :: BIND DEFAULT EVENT HANDLERS (IF NEEDED)
+ $('#details').find('input, textarea').prop('readonly', true);
+
if (bindDefaultEventHandlers) {
BackendCustomers.bindEventHandlers();
}
@@ -32,7 +35,134 @@ var BackendCustomers = {
* Default event handlers declaration for backend customers page.
*/
bindEventHandlers: function() {
-
+ /**
+ * Event: Customer Row "Click"
+ *
+ * Display the customer data of the selected row.
+ */
+ $(document).on('click', '.customer-row', function() {
+ $('#filter-results .selected-row').removeClass('selected-row');
+ $(this).addClass('selected-row');
+
+ var customerId = $(this).attr('data-id');
+ var customer;
+
+ $.each(BackendCustomers.filterResults, function(index, item) {
+ if (item.id === customerId) {
+ customer = item;
+ return;
+ }
+ });
+
+ BackendCustomers.selectedCustomer = customer;
+ BackendCustomers.displayCustomer(customer);
+ $('#edit-customer, #delete-customer').prop('disabled', false);
+ });
+
+ /**
+ * Event: Appointment Row "Click"
+ *
+ * Display appointment data of the selected row.
+ */
+ $(document).on('click', '.appointment-row', function() {
+ $('#customer-appointments .selected-row').removeClass('selected-row');
+ $(this).addClass('selected-row');
+
+ var appointmentId = $(this).attr('data-id');
+ var appointment;
+
+ $.each(BackendCustomers.selectedCustomer.appointments, function(index, item) {
+ if (item.id === appointmentId) {
+ appointment = item;
+ return;
+ }
+ });
+
+ BackendCustomers.selectedAppointment = appointment;
+ BackendCustomers.displayAppointment(appointment);
+ });
+
+ /**
+ * Event: Filter Customers Button "Click"
+ *
+ * Filter customer rows with given string.
+ */
+ $('#filter-customers').click(function() {
+ BackendCustomers.filterCustomers($('#filter-key').val());
+ });
+
+ /**
+ * Event: Add Customer Button "Click"
+ */
+ $('#add-customer').click(function() {
+ BackendCustomers.resetForm();
+ $('#add-edit-delete-group').hide();
+ $('#save-cancel-group').show();
+ $('#details').find('input, textarea').prop('readonly', false);
+ $('#filter-customers').prop('disabled', true);
+ $('.selected-row').removeClass('selected-row');
+ });
+
+ /**
+ * Event: Edit Customer Button "Click"
+ */
+ $('#edit-customer').click(function() {
+ $('#details').find('input, textarea').prop('readonly', false);
+ $('#add-edit-delete-group').hide();
+ $('#save-cancel-group').show();
+ $('#filter-customers').prop('disabled', true);
+ });
+
+ /**
+ * Event: Cancel Customer Add/Edit Operation Button "Click"
+ */
+ $('#cancel-customer').click(function() {
+ $('#details').find('input, textarea').prop('readonly', true);
+ $('#save-cancel-group').hide();
+ $('#add-edit-delete-group').show();
+ $('#filter-customers').prop('disabled', true);
+ });
+
+ /**
+ * Event: Save Add/Edit Customer Operation "Click"
+ */
+ $('#save-customer').click(function() {
+ var customer = {
+ 'first_name': $('#first-name').val(),
+ 'last_name': $('#last-name').val(),
+ 'email': $('#email').val(),
+ 'phone_number': $('#phone-number').val(),
+ 'address': $('#address').val(),
+ 'city': $('#city').val(),
+ 'zip_code': $('#zip-code').val(),
+ 'notes': $('#notes').val()
+ };
+
+ if ($('#customer-id').val() != '') {
+ customer.id = $('#customer-id').val();
+ }
+
+ BackendCustomers.saveCustomer(customer);
+ });
+
+ /**
+ * Event: Delete Customer Button "Click"
+ */
+ $('#delete-customer').click(function() {
+ var messageBtns = {
+ 'Delete': function() {
+ var customerId = BackendCustomers.selectedCustomer.id;
+ BackendCustomers.deleteCustomer(customerId);
+ },
+
+ 'Cancel': function() {
+ $('#message_box').dialog('close');
+ }
+ };
+
+ GeneralFunctions.displayMessageBox('Delete Customer', 'Are you sure that you want '
+ + 'to delete this customer? This action cannot be undone.', messageBtns);
+ });
},
/**
@@ -42,8 +172,34 @@ var BackendCustomers = {
*
* @param {int} customerId Selected customer's record id.
*/
- displayCustomer: function(customerId) {
-
+ displayCustomer: function(customer) {
+ if (customer === undefined) {
+ throw 'DisplayCustomer: customer is undefined';
+ }
+
+ BackendCustomers.resetForm();
+
+ $('#customer-id').val(customer.id);
+ $('#first-name').val(customer.first_name);
+ $('#last-name').val(customer.last_name);
+ $('#email').val(customer.email);
+ $('#phone-number').val(customer.phone_number);
+ $('#address').val(customer.address);
+ $('#city').val(customer.city);
+ $('#zip-code').val(customer.zip_code);
+ $('#notes').val(customer.notes);
+
+ $.each(customer.appointments, function(index, appointment) {
+ var start = Date.parse(appointment.start_datetime).toString('dd/MM/yyyy HH:mm');
+ var end = Date.parse(appointment.end_datetime).toString('dd/MM/yyyy HH:mm');
+ var html =
+ '
' +
+ start + ' - ' + end + '
' +
+ appointment.service.name + ', ' +
+ appointment.provider.first_name + ' ' + appointment.provider.last_name +
+ '
';
+ $('#customer-appointments').append(html);
+ });
},
/**
@@ -53,11 +209,48 @@ var BackendCustomers = {
*
* NOTICE: User the "deleteCustomer" method to delete a customer record.
*
- * @param {object} customerData Contains the customer data. If "id" is not
+ * @param {object} customer Contains the customer data. If "id" is not
* provided then the record is going to be inserted.
*/
- saveCustomer: function(customerData) {
-
+ saveCustomer: function(customer) {
+ if (!BackendCustomers.validateForm()) return;
+
+ var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_customer';
+ var postData = { 'customer': JSON.stringify(customer) };
+
+ $.post(postUrl, postData, function(response) {
+ if (response.exceptions) {
+ response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
+ GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
+ return;
+ }
+
+ if (response.warnings) {
+ response.warnings = GeneralFunctions.parseExceptions(response.warnings);
+ GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
+ }
+
+ $('#add-edit-delete-group').show();
+ $('#save-cancel-group').hide();
+ $('#filter-customers').prop('disabled', false);
+ $('#details').find('input, textarea').prop('readonly', true);
+
+ BackendCustomers.filterCustomers($('#filter-key').val());
+
+ // On edit mode keep the customer data on form.
+ if (customer.id) {
+ $.each(BackendCustomers.filterResults, function(index, item) {
+ if (item.id == customer.id) {
+ customer.appointments = item.appointments; // w/ appointments
+ return;
+ }
+ });
+ BackendCustomers.displayCustomer(customer);
+ $('#edit-customer, #delete-customer').prop('disabled', false);
+ }
+ }, 'json');
},
/**
@@ -67,7 +260,29 @@ var BackendCustomers = {
* @param {int} customerId The customer record id to be deleted.
*/
deleteCustomer: function(customerId) {
-
+ var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_delete_customer';
+ var postData = { 'customer_id': BackendCustomers.selectedCustomer.id };
+
+ $.post(postUrl, postData, function(response) {
+ if (response.exceptions) {
+ response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
+ GeneralFunctions.displayMessageBox('Unexpected Issues', 'Unfortunately the '
+ + 'filter operation could not complete successfully. The following '
+ + 'issues occured.');
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
+ return;
+ }
+
+ if (response.warnings) {
+ response.warnings = GeneralFunctions.parseExceptions(response.warnings);
+ GeneralFunctions.displayMessageBox('Unexpected Warnings', 'The filter operation '
+ + 'complete with the following warnings.');
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
+ }
+
+ $('#message_box').dialog('close');
+ BackendCustomers.filterCustomers($('#filter-key').val());
+ }, 'json');
},
/**
@@ -77,11 +292,15 @@ var BackendCustomers = {
* @param {string} key The filter key string.
*/
filterCustomers: function(key) {
- var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_customers';
+ $('#filter-results').html('');
+ BackendCustomers.resetForm();
+
+ var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_customers';
var postData = { 'key': key };
- $.post(postUrl, postData, function(response) {
+
+ $.post(postUrl, postData, function(response) {
if (response.exceptions) {
- response.exceptions = GeneralFunctions.parseExcpetions(response.exceptions);
+ response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
GeneralFunctions.displayMessageBox('Unexpected Issues', 'Unfortunately the '
+ 'filter operation could not complete successfully. The following '
+ 'issues occured.');
@@ -90,21 +309,23 @@ var BackendCustomers = {
}
if (response.warnings) {
- response.warnings = GeneralFunctions.parseExcpetions(response.warnings);
+ response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox('Unexpected Warnings', 'The filter operation '
+ 'complete with the following warnings.');
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
+
+ BackendCustomers.filterResults = response;
$.each(response, function(index, customer) {
var html =
- '
' +
+ '
' +
'' +
- customer['first_name'] + ' ' + customer['last_name'] +
+ customer.first_name + ' ' + customer.last_name +
'
' +
- '' + customer['email'] + ' | ' +
- '' + customer['phone_number'] + '
' +
- '';
+ '
' + customer.email + ' | ' +
+ '
' + customer.phone_number + '' +
+ '
';
$('#filter-results').append(html);
});
}, 'json');
@@ -116,7 +337,63 @@ var BackendCustomers = {
*
* @return {bool} Returns the validation result.
*/
- validateCustomerForm: function() {
-
- }
+ validateForm: function() {
+ try {
+ $('#form-message').hide();
+ $('.required').css('border', '');
+
+ // :: CHECK REQUIRED FIELDS
+ var missingRequiredField = false;
+ $('.required').each(function() {
+ if ($(this).val() == '') {
+ $(this).css('border', '2px solid red');
+ missingRequiredField = true;
+ }
+ });
+ if (missingRequiredField) {
+ throw 'Fields with * are required!';
+ }
+
+ // :: CHECK EMAIL ADDRESS
+ if (!GeneralFunctions.validateEmail($('#email').val())) {
+ $('#email').css('border', '2px solid red');
+ throw 'Invalid email address!';
+ }
+
+ return true;
+
+ } catch(exc) {
+ $('#form-message').text(exc).show();
+ return false;
+ }
+ },
+
+ /**
+ * Bring the customer data form back to its initial state.
+ */
+ resetForm: function() {
+ $('#details').find('input, textarea').val('');
+ $('#customer-appointments').html('');
+ $('#appointment-details').html('');
+ $('#edit-customer, #delete-customer').prop('disabled', true);
+ },
+
+ /**
+ * Display appointment details on customers backend page.
+ *
+ * @param {object} appointment Appointment data
+ */
+ displayAppointment: function(appointment) {
+ var start = Date.parse(appointment.start_datetime).toString('dd/MM/yyyy HH:mm');
+ var end = Date.parse(appointment.end_datetime).toString('dd/MM/yyyy HH:mm');
+
+ var html =
+ '
' +
+ '' + appointment.service.name + '
' +
+ appointment.provider.first_name + ' ' + appointment.provider.last_name + '
' +
+ start + ' - ' + end + '
' +
+ '
';
+
+ $('#appointment-details').html(html);
+ }
};
\ No newline at end of file