* Optimized js code for the backend pages.

This commit is contained in:
alextselegidis@gmail.com 2013-09-25 15:43:17 +00:00
parent 3450bd93b1
commit 148258385c
17 changed files with 1596 additions and 1409 deletions

View file

@ -474,8 +474,11 @@ class Backend_api extends CI_Controller {
try {
$this->load->model('customers_model');
$customer = json_decode($_POST['customer'], true);
$this->customers_model->add($customer);
echo json_encode(AJAX_SUCCESS);
$customer_id = $this->customers_model->add($customer);
echo json_encode(array(
'status' => AJAX_SUCCESS,
'id' => $customer_id
));
} catch(Exception $exc) {
echo json_encode(array(
'exceptions' => array(exceptionToJavaScript($exc))
@ -509,8 +512,11 @@ class Backend_api extends CI_Controller {
try {
$this->load->model('services_model');
$service = json_decode($_POST['service'], true);
$this->services_model->add($service);
echo json_encode(AJAX_SUCCESS);
$service_id =$this->services_model->add($service);
echo json_encode(array(
'status' => AJAX_SUCCESS,
'id' => $service_id
));
} catch(Exception $exc) {
echo json_encode(array(
'exceptions' => array(exceptionToJavaScript($exc))
@ -568,8 +574,11 @@ class Backend_api extends CI_Controller {
try {
$this->load->model('services_model');
$category = json_decode($_POST['category'], true);
$this->services_model->add_category($category);
echo json_encode(AJAX_SUCCESS);
$category_id = $this->services_model->add_category($category);
echo json_encode(array(
'status' => AJAX_SUCCESS,
'id' => $category_id
));
} catch(Exception $exc) {
echo json_encode(array(
'exceptions' => array(exceptionToJavaScript($exc))
@ -644,15 +653,21 @@ class Backend_api extends CI_Controller {
*
* @param array $_POST['admin'] A json encoded array that contains the admin data. If an 'id'
* value is provided then the record is going to be updated.
* @return string Returns the success contant 'AJAX_SUCCESS' so javascript knows that
* everything completed successfully.
* @return array Returns an array with the operation status and the record id that was
* saved into the database.
*/
public function ajax_save_admin() {
try {
$this->load->model('admins_model');
$admin = json_decode($_POST['admin'], true);
$this->admins_model->add($admin);
echo json_encode(AJAX_SUCCESS);
$admin_id = $this->admins_model->add($admin);
$response = array(
'status' => AJAX_SUCCESS,
'id' => $admin_id
);
echo json_encode($response);
} catch(Exception $exc) {
echo json_encode(array(
'exceptions' => array(exceptionToJavaScript($exc))
@ -722,8 +737,13 @@ class Backend_api extends CI_Controller {
->get_setting('company_working_plan');
}
$this->providers_model->add($provider);
echo json_encode(AJAX_SUCCESS);
$provider_id = $this->providers_model->add($provider);
echo json_encode(array(
'status' => AJAX_SUCCESS,
'id' => $provider_id
));
} catch(Exception $exc) {
echo json_encode(array(
'exceptions' => array(exceptionToJavaScript($exc))
@ -786,8 +806,12 @@ class Backend_api extends CI_Controller {
try {
$this->load->model('secretaries_model');
$secretary = json_decode($_POST['secretary'], true);
$this->secretaries_model->add($secretary);
echo json_encode(AJAX_SUCCESS);
$secretary_id = $this->secretaries_model->add($secretary);
echo json_encode(array(
'status' => AJAX_SUCCESS,
'id' => $secretary_id
));
} catch(Exception $exc) {
echo json_encode(array(
'exceptions' => array(exceptionToJavaScript($exc))

View file

@ -204,16 +204,6 @@ class Secretaries_Model extends CI_Model {
throw new Exception('Invalid email address provided : ' . $secretary['email']);
}
// Validate admin username
if (isset($secretary['settings']['username'])) {
$num_rows = $this->db->get_where('ea_user_settings',
array('username' => $secretary['settings']['username']))->num_rows();
if ($num_rows > 0) {
throw new Exception('Username already exists, please select another '
. 'and try again (username: ' . $secretary['settings']['username'] . ')');
}
}
// Validate admin password
if (isset($secretary['settings']['password'])) {
if (strlen($secretary['settings']['password']) < MIN_PASSWORD_LENGTH) {

View file

@ -18,14 +18,21 @@
</script>
<div id="customers-page" class="row-fluid">
<div id="filter" class="span4">
<div class="input-append">
<input class="span12" type="text" id="filter-key" />
<button type="button" class="btn" id="filter-customers">Filter</button>
</div>
<div id="filter-customers" class="filter-records column span4">
<form class="input-append">
<input class="key span8" type="text" />
<button class="filter btn" type="submit">
<i class="icon-filter"></i>
Filter
</button>
<button class="clear btn" type="button">
<i class="icon-remove-circle"></i>
Clear
</button>
</form>
<h2>Customers</h2>
<div id="filter-results"></div>
<div class="results"></div>
</div>
<div id="details" class="span7 row-fluid">

View file

@ -28,16 +28,24 @@
?>
<div id="services" class="tab-content">
<?php // FILTER SERVICES ?>
<div class="filter span4">
<div class="input-append">
<input class="filter-key span12" type="text" />
<button class="filter-services btn" type="button">Filter</button>
</div>
<div id="filter-services" class="filter-records column span4">
<form class="input-append">
<input class="key span8" type="text" />
<button class="filter btn" type="submit">
<i class="icon-filter"></i>
Filter
</button>
<button class="clear btn" type="button">
<i class="icon-remove-circle"></i>
Clear
</button>
</form>
<h2>Services</h2>
<div class="filter-results"></div>
<div class="results"></div>
</div>
<div class="details span7">
<div class="details column span7">
<div class="btn-toolbar">
<div class="add-edit-delete-group btn-group">
<button id="add-service" class="btn">
@ -97,14 +105,21 @@
// --------------------------------------------------------------
?>
<div id="categories" class="tab-content" style="display:none;">
<div class="filter span4">
<div class="input-append">
<input class="filter-key span12" type="text" class="" />
<button class="filter-categories btn" type="button">Filter</button>
</div>
<div id="filter-categories" class="filter-records column span4">
<form class="input-append">
<input class="key span8" type="text" class="" />
<button class="filter btn" type="submit">
<i class="icon-filter"></i>
Filter
</button>
<button class="clear btn" type="button">
<i class="icon-remove-circle"></i>
Clear
</button>
</form>
<h2>Categories</h2>
<div class="filter-results"></div>
<div class="results"></div>
</div>
<div class="details span7">

View file

@ -1,5 +1,7 @@
<script type="text/javascript"
src="<?php echo $base_url; ?>assets/js/backend_settings.js"></script>
<script type="text/javascript"
src="<?php echo $base_url; ?>assets/js/working_plan.js"></script>
<script type="text/javascript"
src="<?php echo $base_url; ?>assets/js/libs/jquery/jquery-ui-timepicker-addon.js"></script>
<script type="text/javascript"
@ -65,7 +67,7 @@
<a href="<?php echo $this->config->base_url(); ?>" class="btn btn-primary btn-large">
<i class="icon-calendar icon-white"></i>
Visit Book Appointment Page
Book Appointment Page
</a>
</fieldset>
</form>
@ -174,7 +176,7 @@
<br>
<table id="breaks" class="table table-striped">
<table class="breaks table table-striped">
<thead>
<tr>
<th>Day</th>
@ -255,8 +257,8 @@
<br>
<button type="button" id="user-notifications" class="btn" data-toggle="button">
<i class="icon-asterisk"></i>
Receive Email Notifications
<i class="icon-envelope"></i>
Receive Notifications
</button>
</fieldset>
</form>

View file

@ -7,6 +7,9 @@
src="<?php echo $base_url; ?>assets/js/backend_users_providers.js"></script>
<script type="text/javascript"
src="<?php echo $base_url; ?>assets/js/backend_users_secretaries.js"></script>
<script type="text/javascript"
src="<?php echo $base_url; ?>assets/js/working_plan.js"></script>
<script type="text/javascript"
src="<?php echo $base_url; ?>assets/js/libs/jquery/jquery-ui-timepicker-addon.js"></script>
@ -30,25 +33,45 @@
<div id="users-page" class="row-fluid">
<?php // Page Tabs ?>
<?php
// ---------------------------------------------------------------------
//
// Page Navigation
//
// ---------------------------------------------------------------------
?>
<ul class="nav nav-tabs">
<li class="admins-tab tab active"><a>Admins</a></li>
<li class="providers-tab tab"><a>Providers</a></li>
<li class="secretaries-tab tab"><a>Secretaries</a></li>
</ul>
<?php // Admin Tab ?>
<?php
// ---------------------------------------------------------------------
//
// Admins Tab
//
// ---------------------------------------------------------------------
?>
<div id="admins" class="tab-content">
<div class="filter span4">
<div class="input-append">
<input class="filter-key span12" type="text" />
<button class="filter-admins btn" type="button">Filter</button>
</div>
<div id="filter-admins" class="filter-records column span4">
<form class="input-append">
<input class="key span8" type="text" />
<button class="filter btn" type="submit">
<i class="icon-filter"></i>
Filter
</button>
<button class="clear btn" type="button">
<i class="icon-remove-circle"></i>
Clear
</button>
</form>
<h2>Admins</h2>
<div class="filter-results"></div>
<div class="results"></div>
</div>
<div class="details span7">
<div class="details column span7">
<div class="btn-toolbar">
<div class="add-edit-delete-group btn-group">
<button id="add-admin" class="btn">
@ -123,26 +146,40 @@
<br>
<button type="button" id="admin-notifications" class="btn" data-toggle="button">
<i class="icon-asterisk"></i>
<span>Receive Email Notifications</span>
<i class="icon-envelope"></i>
<span>Receive Notifications</span>
</button>
</div>
</div>
</div>
</div>
<?php // Providers Tab ?>
<?php
// ---------------------------------------------------------------------
//
// Providers Tab
//
// ---------------------------------------------------------------------
?>
<div id="providers" class="tab-content" style="display:none;">
<div class="filter span4">
<div class="input-append">
<input class="filter-key span12" type="text" />
<button class="filter-providers btn" type="button">Filter</button>
</div>
<div id="filter-providers" class="filter-records column span4">
<form class="input-append">
<input class="key span8" type="text" />
<button class="filter btn" type="submit">
<i class="icon-filter"></i>
Filter
</button>
<button class="clear btn" type="button">
<i class="icon-remove-circle"></i>
Clear
</button>
</form>
<h2>Providers</h2>
<div class="filter-results"></div>
<div class="results"></div>
</div>
<div class="details span7">
<div class="details column span7">
<div class="btn-toolbar span5">
<div class="add-edit-delete-group btn-group">
<button id="add-provider" class="btn">
@ -167,13 +204,16 @@
</div>
<div class="switch-view pull-right">
<strong>Current View</strong>
<div class="display-details current">Details</div>
<div class="display-working-plan">Working Plan</div>
</div>
<?php // This form message is outside the details view, so that it can be
// visible when the user has working plan view active. ?>
<div class="form-message alert" style="display:none;"></div>
<div class="details-view">
<div class="details-view provider-view">
<h2>Details</h2>
<input type="hidden" id="provider-id" class="record-id" />
@ -223,8 +263,8 @@
<br>
<button type="button" id="provider-notifications" class="btn" data-toggle="button">
<i class="icon-asterisk"></i>
<span>Receive Email Notifications</span>
<i class="icon-envelope"></i>
<span>Receive Notifications</span>
</button>
<br><br>
@ -235,7 +275,7 @@
</div>
</div>
<div class="working-plan-view" style="display: none;">
<div class="working-plan-view provider-view" style="display: none;">
<h2>Working Plan</h2>
<table class="working-plan table table-striped">
<thead>
@ -302,7 +342,7 @@
<br>
<table id="breaks" class="table table-striped">
<table class="breaks table table-striped">
<thead>
<tr>
<th>Day</th>
@ -318,18 +358,32 @@
</div>
</div>
<?php // Secretaries Tab ?>
<?php
// ---------------------------------------------------------------------
//
// Secretaries Tab
//
// ---------------------------------------------------------------------
?>
<div id="secretaries" class="tab-content" style="display:none;">
<div class="filter span4">
<div class="input-append">
<input class="filter-key span12" type="text" />
<button class="filter-secretaries btn" type="button">Filter</button>
</div>
<div id="filter-secretaries" class="filter-records column span4">
<form class="input-append">
<input class="key span8" type="text" />
<button class="filter btn" type="submit">
<i class="icon-filter"></i>
Filter
</button>
<button class="clear btn" type="button">
<i class="icon-remove-circle"></i>
Clear
</button>
</form>
<h2>Secretaries</h2>
<div class="filter-results"></div>
<div class="results"></div>
</div>
<div class="details span7">
<div class="details column span7">
<div class="btn-toolbar">
<div class="add-edit-delete-group btn-group">
<button id="add-secretary" class="btn">
@ -404,7 +458,7 @@
<br>
<button type="button" id="secretary-notifications" class="btn" data-toggle="button">
<i class="icon-asterisk"></i>
<i class="icon-envelope"></i>
<span>Receive Notifications</span>
</button>

View file

@ -194,24 +194,25 @@ body .modal-header h3 {
/* BACKEND CUSTOMERS PAGE
-------------------------------------------------------------------- */
#customers-page #filter {
#customers-page #filter-customers {
margin: 15px 0px 15px 15px;
}
#customers-page #filter-results {
#customers-page .filter-records .results {
overflow-y: auto;
max-height: 650px;
}
#customers-page #filter-results .customer-row {
#customers-page #filter-customers .results .customer-row {
padding: 10px 7px; border-radius: 3px;
}
#customers-page #filter-results .customer-row:hover {
#customers-page #filter-customers .results .customer-row:hover {
background-color: #C6E7D5;
cursor: pointer;
}
#customers-page #filter-results hr {
#customers-page #filter-customers .results hr {
margin: 5px 0;
}
@ -270,6 +271,11 @@ body .modal-header h3 {
cursor: pointer;
}
#services-page .filter-records .results {
overflow-y: auto;
max-height: 650px;
}
#services-page .service-row {
padding: 10px 7px;
border-radius: 3px;
@ -330,6 +336,11 @@ body .modal-header h3 {
cursor: pointer;
}
#users-page .filter-records .results {
overflow-y: auto;
max-height: 650px;
}
#users-page .secretary-row,
#users-page .provider-row,
#users-page .admin-row {
@ -358,7 +369,8 @@ body .modal-header h3 {
#users-page #secretary-notifications.active,
#users-page #provider-notifications.active,
#users-page #admin-notifications.active {
background: #FFFF91;
background: #B6DCFF;
box-shadow: none;
}
#users-page #secretary-providers,
@ -377,11 +389,11 @@ body .modal-header h3 {
#users-page #providers .switch-view div {
display: inline-block;
padding: 10px 15px;
padding: 6px 13px;
border-radius: 4px;
color: #333;
float: left;
margin-right: 5px;
margin-right: 3px;
cursor: pointer;
}
@ -389,6 +401,13 @@ body .modal-header h3 {
background: #F5F5F5;
}
#users-page #providers .switch-view strong {
display: inline-block;
float: left;
margin: 8px 20px;
font-size: 17px;
}
#users-page #providers .switch-view .current {
color: #FFF;
background: #95E4A8;
@ -408,7 +427,7 @@ background: #95E4A8;
clear: both;
}
#users-page #providers #breaks .btn {
#users-page #providers .breaks .btn {
margin-right: 5px;
padding: 4px 7px;
}
@ -437,21 +456,21 @@ padding: 4px 7px;
margin-bottom: 0;
}
#business-logic #breaks .btn {
#business-logic .breaks .btn {
margin-right: 5px;
padding: 4px 7px;
}
#business-logic #breaks input {
#business-logic .breaks input {
margin-bottom: 0;
}
#business-logic #breaks .editable form {
#business-logic .breaks .editable form {
margin: 0;
}
#business-logic #breaks .editable select,
#business-logic #breaks .editable input {
#business-logic .breaks .editable select,
#business-logic .breaks .editable input {
margin: 0;
cursor: pointer;
}
@ -462,4 +481,9 @@ padding: 4px 7px;
#business-logic .ui-spinner {
border: none;
}
#settings-page #user-notifications.active {
background: #B6DCFF;
box-shadow: none;
}

View file

@ -640,7 +640,7 @@ var BackendCalendar = {
$('#enable-sync span').text('Enable Sync');
$('#google-sync').prop('disabled', true);
return;
return false;
}
});
}
@ -1477,7 +1477,7 @@ var BackendCalendar = {
$.each(provider['services'], function(index, serviceId) {
if (serviceId == $dialog.find('#select-service').val()) {
canProvideService = true;
return;
return false;
}
});
@ -1495,7 +1495,7 @@ var BackendCalendar = {
$.each(GlobalVariables.availableServices, function(index, service) {
if (service['id'] == $dialog.find('#select-service').val()) {
serviceDuration = service['duration'];
return;
return false;
}
});

View file

@ -6,26 +6,29 @@
* @namespace BackendCustomers
*/
var BackendCustomers = {
filterResults: {},
selectedCustomer: {},
selectedAppointment: {},
/**
* The page helper contains methods that implement each record type functionality
* (for now there is only the CustomersHelper).
*
* @type {object{
*/
helper: {},
/**
* This method initializes the backend customers page. If you use this namespace
* in a different page do not use this method.
*
* @param {bool} bindDefaultEventHandlers Whether to bind the default event handlers
* or not.
* @param {bool} defaultEventHandlers (OPTIONAL = false) Whether to bind the default
* event handlers or not.
*/
initialize: function(bindDefaultEventHandlers) {
if (bindDefaultEventHandlers === undefined) {
bindDefaultEventHandlers = false; // default value
}
initialize: function(defaultEventHandlers) {
if (defaultEventHandlers == undefined) defaultEventHandlers = false;
BackendCustomers.helper = new CustomersHelper();
BackendCustomers.helper.resetForm();
BackendCustomers.helper.filter('');
BackendCustomers.filterCustomers('');
$('#details').find('input, textarea').prop('readonly', true);
if (bindDefaultEventHandlers) {
if (defaultEventHandlers) {
BackendCustomers.bindEventHandlers();
}
},
@ -34,376 +37,413 @@ 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() {
if ($('#filter-customers').prop('disabled')) {
return; // Do nothing when user edits a customer record.
}
$('#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');
$('#filter-results').css('color', '#AAA');
});
/**
* 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);
$('#filter-results').css('color', '#AAA');
});
/**
* 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', false);
$('#filter-results').css('color', '');
// Reset the selected appointments data.
$('#filter-results .selected-row').trigger('click');
});
/**
* Event: Save Add/Edit Customer Operation "Click"
*/
$('#save-customer').click(function() {
$('#filter-results').css('color', '');
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);
});
},
/**
* This method displays the customer data on the right part of the page.
* When a customer is selected the user can make changes and update the
* customer record.
*
* @param {int} customerId Selected customer's record id.
*/
displayCustomer: function(customer) {
if (customer === undefined) {
throw 'DisplayCustomer: customer is undefined';
CustomersHelper.prototype.bindEventHandlers();
}
};
/**
* This class contains the methods that are used in the backend customers page.
*
* @class CustomersHelper
*/
var CustomersHelper = function() {
this.filterResults = {};
};
/**
* Binds the default event handlers of the backend customers page.
*/
CustomersHelper.prototype.bindEventHandlers = function() {
/**
* Event: Filter Customers Form "Submit"
*/
$('#filter-customers form').submit(function() {
event.preventDefault();
var key = $('#filter-customers .key').val();
$('#filter-customers .selected-row').removeClass('selected-row');
BackendCustomers.helper.resetForm();
BackendCustomers.helper.filter(key);
});
/**
* Event: Filter Customers Clear Button "Click"
*/
$('#filter-customers .clear').click(function() {
$('#filter-customers .key').val('');
BackendCustomers.helper.filter('');
});
/**
* Event: Customer Row "Click"
*
* Display the customer data of the selected row.
*/
$(document).on('click', '.customer-row', function() {
if ($('#filter-customers .filter').prop('disabled')) {
return; // Do nothing when user edits a customer record.
}
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 =
'<div class="appointment-row" data-id="' + appointment.id + '">' +
start + ' - ' + end + '<br>' +
appointment.service.name + ', ' +
appointment.provider.first_name + ' ' + appointment.provider.last_name +
'</div>';
$('#customer-appointments').append(html);
var customerId = $(this).attr('data-id');
var customer = {};
$.each(BackendCustomers.helper.filterResults, function(index, item) {
if (item.id == customerId) {
customer = item;
return false;
}
});
},
/**
* This method makes an ajax call to the server and save the changes of
* an existing customer record, or inserts a new customer row when on insert
* mode.
*
* NOTICE: User the "deleteCustomer" method to delete a customer record.
*
* @param {object} customer Contains the customer data. If "id" is not
* provided then the record is going to be inserted.
*/
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.helper.display(customer);
$('#filter-customers .selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#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 customerId = $('#filter-customers .selected-row').attr('data-id');
var appointmentId = $(this).attr('data-id');
var appointment = {};
$.each(BackendCustomers.helper.filterResults, function(index, c) {
if (c.id === customerId) {
$.each(c.appointments, function(index, a) {
if (a.id == appointmentId) {
appointment = a;
return false;
}
});
BackendCustomers.displayCustomer(customer);
$('#edit-customer, #delete-customer').prop('disabled', false);
return false;
}
}, 'json');
},
/**
* This method makes an ajax call to the server and deletes the selected
* customer record.
*
* @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;
}
BackendCustomers.helper.displayAppointment(appointment);
});
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));
}
/**
* Event: Add Customer Button "Click"
*/
$('#add-customer').click(function() {
BackendCustomers.helper.resetForm();
$('#add-edit-delete-group').hide();
$('#save-cancel-group').show();
$('#details').find('input, textarea').prop('readonly', false);
$('#message_box').dialog('close');
BackendCustomers.filterCustomers($('#filter-key').val());
}, 'json');
},
/**
* This method filters the system registered customers. Pass an empty string
* to display all customers.
*
* @param {string} key The filter key string.
*/
filterCustomers: function(key) {
$('#filter-results').html('');
BackendCustomers.resetForm();
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_customers';
var postData = { 'key': key };
$.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));
}
BackendCustomers.filterResults = response;
$.each(response, function(index, customer) {
var html =
'<div class="customer-row" data-id="' + customer.id + '">' +
'<strong>' +
customer.first_name + ' ' + customer.last_name +
'</strong><br>' +
'<span>' + customer.email + '</span> | ' +
'<span>' + customer.phone_number + '</span>' +
'</div><hr>';
$('#filter-results').append(html);
});
}, 'json');
},
/**
* This method validates the main customer form of the page. There are certain
* rules that the record must fullfil before getting into the system database.
*
* @return {bool} Returns the validation result.
*/
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;
$('#filter-customers button').prop('disabled', true);
$('#filter-customers .results').css('color', '#AAA');
});
/**
* 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 button').prop('disabled', true);
$('#filter-customers .results').css('color', '#AAA');
});
/**
* Event: Cancel Customer Add/Edit Operation Button "Click"
*/
$('#cancel-customer').click(function() {
var id = $('#customer-id').val();
BackendCustomers.helper.resetForm();
if (id != '') {
BackendCustomers.helper.select(id, true);
}
},
/**
* 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
* Event: Save Add/Edit Customer Operation "Click"
*/
displayAppointment: function(appointment) {
$('#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();
}
if (!BackendCustomers.helper.validate(customer)) return;
BackendCustomers.helper.save(customer);
});
/**
* Event: Delete Customer Button "Click"
*/
$('#delete-customer').click(function() {
var customerId = $('#customer-id').val();
var messageBtns = {
'Delete': function() {
BackendCustomers.helper.delete(customerId);
$('#message_box').dialog('close');
},
'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);
});
};
/**
* Save a customer record to the database (via ajax post).
*
* @param {object} customer Contains the customer data.
*/
CustomersHelper.prototype.save = function(customer) {
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_customer';
var postData = { 'customer': JSON.stringify(customer) };
$.post(postUrl, postData, function(response) {
///////////////////////////////////////////////////////////
console.log('Save Customer Response:', response);
///////////////////////////////////////////////////////////
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Customer saved successfully!');
BackendCustomers.helper.resetForm();
$('#filter-customers .key').val('');
BackendCustomers.helper.filter('', response.id, true);
}, 'json');
};
/**
* Delete a customer record from database.
*
* @param {numeric} id Record id to be deleted.
*/
CustomersHelper.prototype.delete = function(id) {
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_delete_customer';
var postData = { 'customer_id': id };
$.post(postUrl, postData, function(response) {
////////////////////////////////////////////////////
//console.log('Delete customer response:', response);
////////////////////////////////////////////////////
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Customer deleted successfully!');
BackendCustomers.helper.resetForm();
BackendCustomers.helper.filter($('#filter-customers .key').val());
}, 'json');
};
/**
* Validate customer data before save (insert or update).
*
* @param {object} customer Contains the customer data.
*/
CustomersHelper.prototype.validate = function(customer) {
$('#form-message').hide();
$('.required').css('border', '');
try {
// Validate required fields.
var missingRequired = false;
$('.required').each(function() {
if ($(this).val() == '') {
$(this).css('border', '2px solid red');
missingRequired = true;
}
});
if (missingRequired) {
throw 'Fields with * are required!';
}
// Validate 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 form back to its initial state.
*/
CustomersHelper.prototype.resetForm = function() {
$('#details').find('input, textarea').val('');
$('#customer-appointments').html('');
$('#appointment-details').html('');
$('#edit-customer, #delete-customer').prop('disabled', true);
$('#add-edit-delete-group').show();
$('#save-cancel-group').hide();
$('#filter-customers button').prop('disabled', false);
$('#filter-customers .selected-row').removeClass('selected-row');
$('#filter-customers .results').css('color', '');
};
/**
* Display a customer record into the form.
*
* @param {object} customer Contains the customer record data.
*/
CustomersHelper.prototype.display = function(customer) {
$('#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);
$('#customer-appointments').empty();
$.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 =
'<div>' +
'<strong>' + appointment.service.name + '</strong><br>' +
appointment.provider.first_name + ' ' + appointment.provider.last_name + '<br>' +
'<div class="appointment-row" data-id="' + appointment.id + '">' +
start + ' - ' + end + '<br>' +
'</div>';
appointment.service.name + ', ' +
appointment.provider.first_name + ' ' + appointment.provider.last_name +
'</div>';
$('#customer-appointments').append(html);
});
};
/**
* Filter customer records.
*
* @param {string} key This key string is used to filter the customer records.
* @param {numeric} selectId (OPTIONAL = undefined) If set then after the filter
* operation the record with the given id will be selected (but not displayed).
* @param {bool} display (OPTIONAL = false) If true then the selected record will
* be displayed on the form.
*/
CustomersHelper.prototype.filter = function(key, selectId, display) {
if (display == undefined) display = false;
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_customers';
var postData = { 'key': key };
$.post(postUrl, postData, function(response) {
///////////////////////////////////////////////////////
console.log('Filter Customers Response:', response);
///////////////////////////////////////////////////////
$('#appointment-details').html(html);
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
BackendCustomers.helper.filterResults = response;
$('#filter-customers .results').html('');
$.each(response, function(index, customer) {
var html = BackendCustomers.helper.getFilterHtml(customer);
$('#filter-customers .results').append(html);
});
if (response.length == 0) {
$('#filter-customers .results').html('<em>No records found...</em>');
}
if (selectId != undefined) {
BackendCustomers.helper.select(selectId, display);
}
}, 'json');
};
/**
* Get the filter results row html code.
*
* @param {object} customer Contains the customer data.
* @return {string} Returns the record html code.
*/
CustomersHelper.prototype.getFilterHtml = function(customer) {
var html =
'<div class="customer-row" data-id="' + customer.id + '">' +
'<strong>' +
customer.first_name + ' ' + customer.last_name +
'</strong><br>' +
'<span>' + customer.email + '</span> | ' +
'<span>' + customer.phone_number + '</span>' +
'</div><hr>';
return html;
};
/**
* Select a specific record from the current filter results. If the customer id does not exist
* in the list then no record will be selected.
*
* @param {numeric} id The record id to be selected from the filter results.
* @param {bool} display (OPTIONAL = false) If true then the method will display the record
* on the form.
*
* @task The selected row must always be visible (even if a vertical scroll bar is used to
* navigate through the filter results).
*/
CustomersHelper.prototype.select = function(id, display) {
if (display == undefined) display = false;
$('#filter-customers .selected-row').removeClass('selected-row');
$('#filter-customers .customer-row').each(function() {
if ($(this).attr('data-id') == id) {
$(this).addClass('selected-row');
return false;
}
});
if (display) {
$.each(BackendCustomers.helper.filterResults, function(index, customer) {
if (customer.id == id) {
BackendCustomers.helper.display(customer);
$('#edit-customer, #delete-customer').prop('disabled', false);
return false;
}
});
}
};
/**
* Display appointment details on customers backend page.
*
* @param {object} appointment Appointment data
*/
CustomersHelper.prototype.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 =
'<div>' +
'<strong>' + appointment.service.name + '</strong><br>' +
appointment.provider.first_name + ' ' + appointment.provider.last_name + '<br>' +
start + ' - ' + end + '<br>' +
'</div>';
$('#appointment-details').html(html);
};

View file

@ -69,227 +69,14 @@ var BackendServices = {
$('.filter-key').val('');
});
/**
* Event: Filter Services Button "Click"
*/
$('.filter-services').click(function() {
var key = $('#services .filter-key').val();
$('.selected-row').removeClass('selected-row');
BackendServices.helper.resetForm();
BackendServices.helper.filter(key);
});
ServicesHelper.prototype.bindEventHandlers();
CategoriesHelper.prototype.bindEventHandlers();
/**
* Event: Filter Categories Button "Click"
*/
$('.filter-categories').click(function() {
var key = $('#categories .filter-key').val();
$('.selected-row').removeClass('selected-row');
BackendServices.helper.resetForm();
BackendServices.helper.filter(key);
});
/**
* Event: Filter Service Row "Click"
*
* Display the selected service data to the user.
*/
$(document).on('click', '.service-row', function() {
if ($('#services .filter-services').prop('disabled')) {
$('#services .filter-results').css('color', '#AAA');
return; // exit because we are on edit mode
}
var service = { 'id': $(this).attr('data-id') };
$.each(BackendServices.helper.filterResults, function(index, item) {
if (item.id === service.id) {
service = item;
return;
}
});
BackendServices.helper.display(service);
$('.selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#edit-service, #delete-service').prop('disabled', false);
});
/**
* Event: Filter Categories Row "Click"
*
* Displays the selected row data on the right side of the page.
*/
$(document).on('click', '.category-row', function() {
if ($('#categories .filter-categories').prop('disabled')) {
$('#categories .filter-results').css('color', '#AAA');
return; // exit because we are on edit mode
}
var category = { 'id': $(this).attr('data-id') };
$.each(BackendServices.helper.filterResults, function(index, item) {
if (item.id === category.id) {
category = item;
return;
}
});
BackendServices.helper.display(category);
$('.selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#edit-category, #delete-category').prop('disabled', false);
});
/**
* Event: Add New Service Button "Click"
*/
$('#add-service').click(function() {
BackendServices.helper.resetForm();
$('#services .add-edit-delete-group').hide();
$('#services .save-cancel-group').show();
$('#services .details').find('input, textarea').prop('readonly', false);
$('#services .details').find('select').prop('disabled', false);
$('#service-duration').spinner('enable');
$('#services .filter-services').prop('disabled', true);
$('#services .filter-results').css('color', '#AAA');
});
/**
* Event: Add Category Button "Click"
*/
$('#add-category').click(function() {
BackendServices.helper.resetForm();
$('#categories .add-edit-delete-group').hide();
$('#categories .save-cancel-group').show();
$('#categories .details').find('input, textarea').prop('readonly', false);
$('#categories .filter-categories').prop('disabled', true);
$('#categories .filter-results').css('color', '#AAA');
});
/**
* Event: Cancel Service Button "Click"
*
* Cancel add or edit of a service record.
*/
$('#cancel-service').click(function() {
BackendServices.helper.resetForm();
});
/**
* Event: Cancel Category Button "Click"
*/
$('#cancel-category').click(function() {
BackendServices.helper.resetForm();
});
/**
* Event: Save Service Button "Click"
*/
$('#save-service').click(function() {
var service = {
'name': $('#service-name').val(),
'duration': $('#service-duration').val(),
'price': $('#service-price').val(),
'currency': $('#service-currency').val(),
'description': $('#service-description').val()
};
if ($('#service-category').val() !== 'null') {
service.id_service_categories = $('#service-category').val();
} else {
service.id_service_categories = null;
}
if ($('#service-id').val() !== '') {
service.id = $('#service-id').val();
}
if (!BackendServices.helper.validate(service)) return;
BackendServices.helper.save(service);
});
/**
* Event: Categories Save Button "Click"
*/
$('#save-category').click(function() {
var category = {
'name': $('#category-name').val(),
'description': $('#category-description').val()
};
if ($('#category-id').val() !== '') {
category.id = $('#category-id').val();
}
if (!BackendServices.helper.validate(category)) return;
BackendServices.helper.save(category);
});
/**
* Event: Edit Service Button "Click"
*/
$('#edit-service').click(function() {
$('#services .add-edit-delete-group').hide();
$('#services .save-cancel-group').show();
$('.filter-services').prop('disabled', true);
$('#services .filter-results').css('color', '#AAA');
$('#services .details').find('input, textarea').prop('readonly', false);
$('#services .details select').prop('disabled', false);
$('#service-duration').spinner('enable');
});
/**
* Event: Edit Category Button "Click"
*/
$('#edit-category').click(function() {
$('#categories .add-edit-delete-group').hide();
$('#categories .save-cancel-group').show();
$('.filter-categories').prop('disabled', true);
$('#categories .filter-results').css('color', '#AAA');
$('#categories .details').find('input, textarea').prop('readonly', false);
});
/**
* Event: Delete Service Button "Click"
*/
$('#delete-service').click(function() {
var serviceId = $('#service-id').val();
var messageBtns = {
'Delete': function() {
BackendServices.helper.delete(serviceId);
$('#message_box').dialog('close');
},
'Cancel': function() {
$('#message_box').dialog('close');
}
};
GeneralFunctions.displayMessageBox('Delete Service', 'Are you sure that you want '
+ 'to delete this record? This action cannot be undone.', messageBtns);
});
$('#delete-category').click(function() {
var categoryId = $('#category-id').val();
var messageBtns = {
'Delete': function() {
BackendServices.helper.delete(categoryId);
$('#message_box').dialog('close');
},
'Cancel': function() {
$('#message_box').dialog('close');
}
};
GeneralFunctions.displayMessageBox('Delete Category', 'Are you sure that you want '
+ 'to delete this record? This action cannot be undone.', messageBtns);
});
},
/**
* Update the service category listbox. Use this method every time a change is made
* to the service categories db table.
*
* @param {array} categories Contains the available category objects.
*/
updateAvailableCategories: function() {
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_service_categories';
@ -322,6 +109,142 @@ var ServicesHelper = function() {
this.filterResults = {};
};
ServicesHelper.prototype.bindEventHandlers = function() {
/**
* Event: Filter Services Form "Submit"
*/
$('#filter-services form').submit(function() {
event.preventDefault();
var key = $('#filter-services .key').val();
$('#filter-services .selected-row').removeClass('selected-row');
BackendServices.helper.resetForm();
BackendServices.helper.filter(key);
});
/**
* Event: Filter Service Cancel Button "Click"
*/
$('#filter-services .clear').click(function() {
$('#filter-services .key').val('');
BackendServices.helper.filter('');
});
/**
* Event: Filter Service Row "Click"
*
* Display the selected service data to the user.
*/
$(document).on('click', '.service-row', function() {
if ($('#filter-services .filter').prop('disabled')) {
$('#filter-services .results').css('color', '#AAA');
return; // exit because we are on edit mode
}
var serviceId = $(this).attr('data-id');
var service = {};
$.each(BackendServices.helper.filterResults, function(index, item) {
if (item.id === serviceId) {
service = item;
return false;
}
});
BackendServices.helper.display(service);
$('#filter-services .selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#edit-service, #delete-service').prop('disabled', false);
});
/**
* Event: Add New Service Button "Click"
*/
$('#add-service').click(function() {
BackendServices.helper.resetForm();
$('#services .add-edit-delete-group').hide();
$('#services .save-cancel-group').show();
$('#services .details').find('input, textarea').prop('readonly', false);
$('#services .details').find('select').prop('disabled', false);
$('#service-duration').spinner('enable');
$('#filter-services button').prop('disabled', true);
$('#filter-services .results').css('color', '#AAA');
});
/**
* Event: Cancel Service Button "Click"
*
* Cancel add or edit of a service record.
*/
$('#cancel-service').click(function() {
var id = $('#service-id').val();
BackendServices.helper.resetForm();
if (id != '') {
BackendServices.helper.select(id, true);
}
});
/**
* Event: Save Service Button "Click"
*/
$('#save-service').click(function() {
var service = {
'name': $('#service-name').val(),
'duration': $('#service-duration').val(),
'price': $('#service-price').val(),
'currency': $('#service-currency').val(),
'description': $('#service-description').val()
};
if ($('#service-category').val() !== 'null') {
service.id_service_categories = $('#service-category').val();
} else {
service.id_service_categories = null;
}
if ($('#service-id').val() !== '') {
service.id = $('#service-id').val();
}
if (!BackendServices.helper.validate(service)) return;
BackendServices.helper.save(service);
});
/**
* Event: Edit Service Button "Click"
*/
$('#edit-service').click(function() {
$('#services .add-edit-delete-group').hide();
$('#services .save-cancel-group').show();
$('#services .details').find('input, textarea').prop('readonly', false);
$('#services .details select').prop('disabled', false);
$('#service-duration').spinner('enable');
$('#filter-services button').prop('disabled', true);
$('#filter-services .results').css('color', '#AAA');
});
/**
* Event: Delete Service Button "Click"
*/
$('#delete-service').click(function() {
var serviceId = $('#service-id').val();
var messageBtns = {
'Delete': function() {
BackendServices.helper.delete(serviceId);
$('#message_box').dialog('close');
},
'Cancel': function() {
$('#message_box').dialog('close');
}
};
GeneralFunctions.displayMessageBox('Delete Service', 'Are you sure that you want '
+ 'to delete this record? This action cannot be undone.', messageBtns);
});
};
/**
* Save service record to database.
*
@ -329,23 +252,30 @@ var ServicesHelper = function() {
* then the update operation is going to be executed.
*/
ServicesHelper.prototype.save = function(service) {
////////////////////////////////////////////////
//console.log('Service data to save:', service);
////////////////////////////////////////////////
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_service';
var postData = { 'service': JSON.stringify(service) };
$.post(postUrl, postData, function(response) {
console.log('Save Service Response:', response);
//////////////////////////////////////////////////
//console.log('Save Service Response:', response);
//////////////////////////////////////////////////
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Service saved successfully!');
BackendServices.helper.resetForm();
BackendServices.helper.filter($('#services .filter-key').val());
$('#filter-services .key').val('');
BackendServices.helper.filter('', response.id, true);
}, 'json');
};
/**
* Delete a service records from database.
* Delete a service record from database.
*
* @param {int} id Record id to be deleted.
* @param {numeric} id Record id to be deleted.
*/
ServicesHelper.prototype.delete = function(id) {
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_delete_service';
@ -353,7 +283,7 @@ ServicesHelper.prototype.delete = function(id) {
$.post(postUrl, postData, function(response) {
////////////////////////////////////////////////////
console.log('Delete service response:', response);
//console.log('Delete service response:', response);
////////////////////////////////////////////////////
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
@ -361,7 +291,7 @@ ServicesHelper.prototype.delete = function(id) {
Backend.displayNotification('Service deleted successfully!');
BackendServices.helper.resetForm();
BackendServices.helper.filter($('#services .filter-key').val());
BackendServices.helper.filter($('#filter-services .key').val());
});
};
@ -404,8 +334,10 @@ ServicesHelper.prototype.resetForm = function() {
$('#edit-service, #delete-service').prop('disabled', true);
$('#services .details').find('input, textarea').prop('readonly', true);
$('#service-category').prop('disabled', true);
$('.filter-services').prop('disabled', false);
$('#services .filter-results').css('color', '');
$('#filter-services .selected-row').removeClass('selected-row');
$('#filter-services button').prop('disabled', false);
$('#filter-services .results').css('color', '');
};
/**
@ -429,25 +361,39 @@ ServicesHelper.prototype.display = function(service) {
* Filters service records depending a string key.
*
* @param {string} key This is used to filter the service records of the database.
* @param {numeric} selectId (OPTIONAL = undefined) If set then after the filter
* operation the record with this id will be selected (but not displayed).
* @param {bool} display (OPTIONAL = false) If true then the selected record will
* be displayed on the form.
*/
ServicesHelper.prototype.filter = function(key) {
ServicesHelper.prototype.filter = function(key, selectId, display) {
if (display == undefined) display = false;
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_services';
var postData = { 'key': key };
$.post(postUrl, postData, function(response) {
/////////////////////////////////////////////////////
console.log('Filter services response:', response);
//console.log('Filter services response:', response);
/////////////////////////////////////////////////////
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
BackendServices.helper.filterResults = response;
$('#services .filter-results').html('');
$('#filter-services .results').html('');
$.each(response, function(index, service) {
var html = ServicesHelper.prototype.getFilterHtml(service);
$('#services .filter-results').append(html);
$('#filter-services .results').append(html);
});
if (response.length == 0) {
$('#filter-services .result').html('<em>No results found ...</em>');
}
if (selectId != undefined) {
BackendServices.helper.select(selectId, display);
}
}, 'json');
};
@ -468,6 +414,40 @@ ServicesHelper.prototype.getFilterHtml = function(service) {
return html;
};
/**
* Select a specific record from the current filter results. If the service id does not exist
* in the list then no record will be selected.
*
* @param {numeric} id The record id to be selected from the filter results.
* @param {bool} display (OPTIONAL = false) If true then the method will display the record
* on the form.
*
* @task The selected row must always be visible (even if a vertical scroll bar is used to
* navigate through the filter results).
*/
ServicesHelper.prototype.select = function(id, display) {
if (display == undefined) display = false;
$('#filter-services .selected-row').removeClass('selected-row');
$('#filter-services .service-row').each(function() {
if ($(this).attr('data-id') == id) {
$(this).addClass('selected-row');
return false;
}
});
if (display) {
$.each(BackendServices.helper.filterResults, function(index, service) {
if (service.id == id) {
BackendServices.helper.display(service);
$('#edit-service, #delete-service').prop('disabled', false);
return false;
}
});
}
};
/**
* This class contains the core method implementations that belong to the categories tab
* of the backend services page.
@ -478,12 +458,139 @@ var CategoriesHelper = function() {
this.filterResults = {};
};
/**
* Binds the default event handlers of the categories tab.
*/
CategoriesHelper.prototype.bindEventHandlers = function() {
/**
* Event: Filter Categories Cancel Button "Click"
*/
$('#filter-categories .clear').click(function() {
$('#filter-categories .key').val('');
BackendServices.helper.filter('');
});
/**
* Event: Filter Categories Form "Submit"
*/
$('#filter-categories form').submit(function() {
event.preventDefault();
var key = $('#filter-categories .key').val();
$('.selected-row').removeClass('selected-row');
BackendServices.helper.resetForm();
BackendServices.helper.filter(key);
});
/**
* Event: Filter Categories Row "Click"
*
* Displays the selected row data on the right side of the page.
*/
$(document).on('click', '.category-row', function() {
if ($('#filter-categories .filter').prop('disabled')) {
$('#filter-categories .results').css('color', '#AAA');
return; // exit because we are on edit mode
}
var categoryId = $(this).attr('data-id');
var category = {};
$.each(BackendServices.helper.filterResults, function(index, item) {
if (item.id === categoryId) {
category = item;
return false;
}
});
BackendServices.helper.display(category);
$('#filter-categories .selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#edit-category, #delete-category').prop('disabled', false);
});
/**
* Event: Add Category Button "Click"
*/
$('#add-category').click(function() {
BackendServices.helper.resetForm();
$('#categories .add-edit-delete-group').hide();
$('#categories .save-cancel-group').show();
$('#categories .details').find('input, textarea').prop('readonly', false);
$('#filter-categories button').prop('disabled', true);
$('#filter-categories .results').css('color', '#AAA');
});
/**
* Event: Edit Category Button "Click"
*/
$('#edit-category').click(function() {
$('#categories .add-edit-delete-group').hide();
$('#categories .save-cancel-group').show();
$('#categories .details').find('input, textarea').prop('readonly', false);
$('#filter-categories button').prop('disabled', true);
$('#filter-categories .results').css('color', '#AAA');
});
/**
* Event: Delete Category Button "Click"
*/
$('#delete-category').click(function() {
var categoryId = $('#category-id').val();
var messageBtns = {
'Delete': function() {
BackendServices.helper.delete(categoryId);
$('#message_box').dialog('close');
},
'Cancel': function() {
$('#message_box').dialog('close');
}
};
GeneralFunctions.displayMessageBox('Delete Category', 'Are you sure that you want '
+ 'to delete this record? This action cannot be undone.', messageBtns);
});
/**
* Event: Categories Save Button "Click"
*/
$('#save-category').click(function() {
var category = {
'name': $('#category-name').val(),
'description': $('#category-description').val()
};
if ($('#category-id').val() !== '') {
category.id = $('#category-id').val();
}
if (!BackendServices.helper.validate(category)) return;
BackendServices.helper.save(category);
});
/**
* Event: Cancel Category Button "Click"
*/
$('#cancel-category').click(function() {
var id = $('#category-id').val();
BackendServices.helper.resetForm();
if (id != '') {
BackendServices.helper.select(id, true);
}
});
};
/**
* Filter service categories records.
*
* @param {string} key This key string is used to filter the category records.
* @param {numeric} selectId (OPTIONAL = undefined) If set then after the filter
* operation the record with the given id will be selected (but not displayed).
* @param {bool} display (OPTIONAL = false) If true then the selected record will
* be displayed on the form.
*/
CategoriesHelper.prototype.filter = function(key) {
CategoriesHelper.prototype.filter = function(key, selectId, display) {
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_service_categories';
var postData = { 'key': key };
@ -495,12 +602,21 @@ CategoriesHelper.prototype.filter = function(key) {
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
BackendServices.helper.filterResults = response;
$('#categories .filter-results').html('');
$('#filter-categories .results').html('');
$.each(response, function(index, category) {
var html = BackendServices.helper.getFilterHtml(category);
$('#categories .filter-results').append(html);
$('#filter-categories .results').append(html);
});
if (response.length == 0) {
$('#filter-categories .results').html('<em>No records found...</em>');
}
if (selectId != undefined) {
BackendServices.helper.select(selectId, display);
}
}, 'json');
};
@ -522,7 +638,8 @@ CategoriesHelper.prototype.save = function(category) {
Backend.displayNotification('Service saved successfully!');
BackendServices.helper.resetForm();
BackendServices.helper.filter($('#categories .filter-key').val());
$('#filter-categories .key').val('');
BackendServices.helper.filter('', response.id, true);
BackendServices.updateAvailableCategories();
});
};
@ -546,7 +663,7 @@ CategoriesHelper.prototype.delete = function(id) {
Backend.displayNotification('Category deleted successfully!');
BackendServices.helper.resetForm();
BackendServices.helper.filter($('#categories .filter-key').val());
BackendServices.helper.filter($('#filter-categories .key').val());
BackendServices.updateAvailableCategories();
});
};
@ -597,8 +714,10 @@ CategoriesHelper.prototype.resetForm = function() {
$('#categories .details').find('input, textarea').val('');
$('#categories .details').find('input, textarea').prop('readonly', true);
$('#edit-category, #delete-category').prop('disabled', true);
$('#categories .filter-results').css('color', '');
$('#categories .filter-categories').prop('disabled', false);
$('#filter-categories .selected-row').removeClass('selected-row');
$('#filter-categories .results').css('color', '');
$('#filter-categories button').prop('disabled', false);
};
/**
@ -616,3 +735,36 @@ CategoriesHelper.prototype.getFilterHtml = function(category) {
return html;
};
/**
* Select a specific record from the current filter results. If the category id does not exist
* in the list then no record will be selected.
*
* @param {numeric} id The record id to be selected from the filter results.
* @param {bool} display (OPTIONAL = false) If true then the method will display the record
* on the form.
*
* @task The selected row must always be visible (even if a vertical scroll bar is used to
* navigate through the filter results).
*/
CategoriesHelper.prototype.select = function(id, display) {
if (display == undefined) display = false;
$('#filter-categories .selected-row').removeClass('selected-row');
$('#filter-categories .category-row').each(function() {
if ($(this).attr('data-id') == id) {
$(this).addClass('selected-row');
return false;
}
});
if (display) {
$.each(BackendServices.helper.filterResults, function(index, category) {
if (category.id == id) {
BackendServices.helper.display(category);
$('#edit-category, #delete-category').prop('disabled', false);
return false;
}
});
}
};

View file

@ -10,20 +10,10 @@ var BackendSettings = {
SETTINGS_USER: 'SETTINGS_USER',
/**
* This flag is used when trying to cancel row editing. It is
* true only whenever the user presses the cancel button.
*
* @type {bool}
* Use this WorkingPlan class instance to perform actions on the page's working plan
* tables.
*/
enableCancel: false,
/**
* This flag determines whether the jeditables are allowed to submit. It is
* true only whenever the user presses the save button.
*
* @type {bool}
*/
enableSubmit: false,
wp: {},
/**
* Tab settings object.
@ -55,60 +45,9 @@ var BackendSettings = {
}
});
$.each(workingPlan, function(index, workingDay) {
if (workingDay != null) {
$('#' + index).prop('checked', true);
$('#' + index + '-start').val(workingDay.start);
$('#' + index + '-end').val(workingDay.end);
// Add the day's breaks on the breaks table.
$.each(workingDay.breaks, function(i, brk) {
var tr =
'<tr>' +
'<td class="break-day editable">' + GeneralFunctions.ucaseFirstLetter(index) + '</td>' +
'<td class="break-start editable">' + brk.start + '</td>' +
'<td class="break-end editable">' + brk.end + '</td>' +
'<td>' +
'<button type="button" class="btn edit-break" title="Edit Break">' +
'<i class="icon-pencil"></i>' +
'</button>' +
'<button type="button" class="btn delete-break" title="Delete Break">' +
'<i class="icon-remove"></i>' +
'</button>' +
'<button type="button" class="btn save-break hidden" title="Save Break">' +
'<i class="icon-ok"></i>' +
'</button>' +
'<button type="button" class="btn cancel-break hidden" title="Cancel Break">' +
'<i class="icon-ban-circle"></i>' +
'</button>' +
'</td>' +
'</tr>';
$('#breaks').append(tr);
});
} else {
$('#' + index).prop('checked', false);
$('#' + index + '-start').prop('disabled', true);
$('#' + index + '-end').prop('disabled', true);
}
});
// Make break cells editable.
BackendSettings.editableBreakDay($('#breaks .break-day'));
BackendSettings.editableBreakTime($('#breaks').find('.break-start, .break-end'));
// Set timepickers where needed.
$('.working-plan input').timepicker({
'timeFormat': 'HH:mm',
'onSelect': function(datetime, inst) {
// Start time must be earlier than end time.
var start = Date.parse($(this).parent().parent().find('.work-start').val());
var end = Date.parse($(this).parent().parent().find('.work-end').val());
if (start > end) {
$(this).parent().parent().find('.work-end').val(start.addHours(1).toString('HH:mm'));
}
}
});
BackendSettings.wp = new WorkingPlan();
BackendSettings.wp.setup(workingPlan);
BackendSettings.wp.timepickers(false);
// Book Advance Timeout Spinner
$('#book-advance-timeout').spinner({
@ -150,6 +89,8 @@ var BackendSettings = {
* backend/settings html, so do not use this method on a different page.
*/
bindEventHandlers: function() {
BackendSettings.wp.bindEventHandlers();
/**
* Event: Tab "Click"
*
@ -196,177 +137,6 @@ var BackendSettings = {
console.log('Settings To Save: ', settings);
//////////////////////////////////////////////
});
/**
* Event: Day Checkbox "Click"
*
* Enable or disable the time selection for each day.
*/
$('.working-plan input[type="checkbox"]').click(function() {
var id = $(this).attr('id');
if ($(this).prop('checked') == true) {
$('#' + id + '-start').prop('disabled', false).val('09:00');
$('#' + id + '-end').prop('disabled', false).val('18:00');
} else {
$('#' + id + '-start').prop('disabled', true).val('');
$('#' + id + '-end').prop('disabled', true).val('');
}
});
/**
* Event: Add Break Button "Click"
*
* A new row is added on the table and the user can enter the new break
* data. After that he can either press the save or cancel button.
*/
$('.add-break').click(function() {
var tr =
'<tr>' +
'<td class="break-day editable">Monday</td>' +
'<td class="break-start editable">09:00</td>' +
'<td class="break-end editable">10:00</td>' +
'<td>' +
'<button type="button" class="btn edit-break" title="Edit Break">' +
'<i class="icon-pencil"></i>' +
'</button>' +
'<button type="button" class="btn delete-break" title="Delete Break">' +
'<i class="icon-remove"></i>' +
'</button>' +
'<button type="button" class="btn save-break hidden" title="Save Break">' +
'<i class="icon-ok"></i>' +
'</button>' +
'<button type="button" class="btn cancel-break hidden" title="Cancel Break">' +
'<i class="icon-ban-circle"></i>' +
'</button>' +
'</td>' +
'</tr>';
$('#breaks').prepend(tr);
// Bind editable and event handlers.
tr = $('#breaks tr').get()[1];
BackendSettings.editableBreakDay($(tr).find('.break-day'));
BackendSettings.editableBreakTime($(tr).find('.break-start, .break-end'));
$(tr).find('.edit-break').trigger('click');
});
/**
* Event: Edit Break Button "Click"
*
* Enables the row editing for the "Breaks" table rows.
*/
$(document).on('click', '.edit-break', function() {
// Reset previous editable tds
var $previousEdt = $(this).closest('table').find('.editable').get();
$.each($previousEdt, function(index, edt) {
edt.reset();
});
// Make all cells in current row editable.
$(this).parent().parent().children().trigger('edit');
$(this).parent().parent().find('.break-start input, .break-end input').timepicker();
$(this).parent().parent().find('.break-day select').focus();
// Show save - cancel buttons.
$(this).closest('table').find('.edit-break, .delete-break').addClass('hidden');
$(this).parent().find('.save-break, .cancel-break').removeClass('hidden');
});
/**
* Event: Delete Break Button "Click"
*
* Removes the current line from the "Breaks" table.
*/
$(document).on('click', '.delete-break', function() {
$(this).parent().parent().remove();
});
/**
* Event: Cancel Break Button "Click"
*
* Bring the "#breaks" table back to its initial state.
*/
$(document).on('click', '.cancel-break', function() {
BackendSettings.enableCancel = true;
$(this).parent().parent().find('.cancel-editable').trigger('click');
BackendSettings.enableCancel = false;
$(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
$(this).parent().find('.save-break, .cancel-break').addClass('hidden');
});
/**
* Event: Save Break Button "Click"
*
* Save the editable values and restore the table to its initial state.
*/
$(document).on('click', '.save-break', function() {
// Break's start time must always be prior to break's end.
var start = Date.parse($(this).parent().parent().find('.break-start input').val());
var end = Date.parse($(this).parent().parent().find('.break-end input').val());
if (start > end) {
$(this).parent().parent().find('.break-end input').val(start.addHours(1).toString('HH:mm'));
}
BackendSettings.enableSubmit = true;
$(this).parent().parent().find('.editable .submit-editable').trigger('click');
BackendSettings.enableSubmit = false;
$(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
$(this).parent().find('.save-break, .cancel-break').addClass('hidden');
});
},
/**
* Initialize the editable functionality to the break day table cells.
*
* @param {object} $selector The cells to be initialized.
*/
editableBreakDay: function($selector) {
$selector.editable(function(value, settings) {
return value;
}, {
'type': 'select',
'data': '{ "Monday": "Monday", "Tuesday": "Tuesday", "Wednesday": "Wednesday", '
+ '"Thursday": "Thursday", "Friday": "Friday", "Saturday": "Saturday", '
+ '"Sunday": "Sunday", "selected": "Monday"}',
'event': 'edit',
'height': '30px',
'submit': '<button type="button" class="hidden submit-editable">Submit</button>',
'cancel': '<button type="button" class="hidden cancel-editable">Cancel</button>',
'onblur': 'ignore',
'onreset': function(settings, td) {
if (!BackendSettings.enableCancel) return false; // disable ESC button
},
'onsubmit': function(settings, td) {
if (!BackendSettings.enableSubmit) return false; // disable Enter button
}
});
},
/**
* Initialize the editable functionality to the break time table cells.
*
* @param {object} $selector The cells to be initialized.
*/
editableBreakTime: function($selector) {
$selector.editable(function(value, settings) {
// Do not return the value because the user needs to press the "Save" button.
return value;
}, {
'event': 'edit',
'height': '25px',
'submit': '<button type="button" class="hidden submit-editable">Submit</button>',
'cancel': '<button type="button" class="hidden cancel-editable">Cancel</button>',
'onblur': 'ignore',
'onreset': function(settings, td) {
if (!BackendSettings.enableCancel) return false; // disable ESC button
},
'onsubmit': function(settings, td) {
if (!BackendSettings.enableSubmit) return false; // disable Enter button
}
});
}
};
@ -418,37 +188,9 @@ SystemSettings.prototype.get = function() {
});
// Business Logic Tab
var workingPlan = {};
$('.working-plan input[type="checkbox"').each(function() {
var id = $(this).attr('id');
if ($(this).prop('checked') == true) {
workingPlan[id] = {}
workingPlan[id].start = $('#' + id + '-start').val();
workingPlan[id].end = $('#' + id + '-end').val();
workingPlan[id].breaks = [];
$('#breaks tr').each(function(index, tr) {
var day = $(tr).find('.break-day').text().toLowerCase();
if (day == id) {
var start = $(tr).find('.break-start').text();
var end = $(tr).find('.break-end').text();
workingPlan[id].breaks.push({
'start': start,
'end': end
});
}
});
} else {
workingPlan[id] = null;
}
});
settings.push({
'name': 'company_working_plan',
'value': JSON.stringify(workingPlan)
'value': JSON.stringify(BackendSettings.wp.get())
});
settings.push({

View file

@ -13,22 +13,13 @@ var BackendUsers = {
* @type AdminsHelper|ProvidersHelper|SecretariesHelper
*/
helper: {},
/**
* This flag is used when trying to cancel row editing. It is
* true only whenever the user presses the cancel button.
*
* @type {bool}
*/
enableCancel: false,
/**
* This flag determines whether the jeditables are allowed to submit. It is
* true only whenever the user presses the save button.
* Use this class instance for performing actions on the working plan.
*
* @type {bool}
* @type {object}
*/
enableSubmit: false,
wp: {},
/**
* Initialize the backend users page.
@ -44,6 +35,9 @@ var BackendUsers = {
BackendUsers.helper.resetForm();
BackendUsers.helper.filter('');
BackendUsers.wp = new WorkingPlan();
BackendUsers.wp.bindEventHandlers();
// Fill the services and providers list boxes.
$.each(GlobalVariables.services, function(index, service) {
var html = '<label class="checkbox"><input type="checkbox" data-id="' + service.id + '" />'
@ -150,7 +144,7 @@ var BackendUsers = {
}, 'json');
});
// -----------------------------------------------------------------
// ------------------------------------------------------------------------
AdminsHelper.prototype.bindEventHandlers();
@ -161,5 +155,7 @@ var BackendUsers = {
// ------------------------------------------------------------------------
SecretariesHelper.prototype.bindEventHandlers();
// ------------------------------------------------------------------------
}
};

View file

@ -14,16 +14,25 @@ var AdminsHelper = function() {
*/
AdminsHelper.prototype.bindEventHandlers = function() {
/**
* Event: Filter Admins Button "Click"
* Event: Filter Admins Form "Sumbit"
*
* Filter the admin records with the given key string.
*/
$('.filter-admins').click(function() {
var key = $('#admins .filter-key').val();
$('.selected-row').removeClass('selected-row');
$('#filter-admins form').submit(function() {
event.preventDefault();
var key = $('#filter-admins .key').val();
$('#filter-admins .selected-row').removeClass('selected-row');
BackendUsers.helper.resetForm();
BackendUsers.helper.filter(key);
});
/**
* Event: Clear Filter Results Button "Click"
*/
$('#filter-admins .clear').click(function() {
BackendUsers.helper.filter('');
$('#filter-admins .key').val('');
});
/**
* Event: Filter Admin Row "Click"
@ -31,20 +40,22 @@ AdminsHelper.prototype.bindEventHandlers = function() {
* Display the selected admin data to the user.
*/
$(document).on('click', '.admin-row', function() {
if ($('#admins .filter-admins').prop('disabled')) {
$('#admins .filter-results').css('color', '#AAA');
if ($('#filter-admins .filter').prop('disabled')) {
$('#filter-admins .results').css('color', '#AAA');
return; // exit because we are currently on edit mode
}
var admin = { 'id': $(this).attr('data-id') };
var adminId = $(this).attr('data-id');
var admin = {};
$.each(BackendUsers.helper.filterResults, function(index, item) {
if (item.id === admin.id) {
if (item.id === adminId) {
admin = item;
return;
return false;
}
});
BackendUsers.helper.display(admin);
$('.selected-row').removeClass('selected-row');
$('#filter-admins .selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#edit-admin, #delete-admin').prop('disabled', false);
});
@ -57,10 +68,10 @@ AdminsHelper.prototype.bindEventHandlers = function() {
$('#admins .add-edit-delete-group').hide();
$('#admins .save-cancel-group').show();
$('#admins .details').find('input, textarea').prop('readonly', false);
$('#admins .filter-admins').prop('disabled', true);
$('#admins .filter-results').css('color', '#AAA');
$('#admin-password, #admin-password-confirm').addClass('required');
$('#admin-notifications').prop('disabled', false);
$('#filter-admins button').prop('disabled', true);
$('#filter-admins .results').css('color', '#AAA');
});
/**
@ -69,11 +80,12 @@ AdminsHelper.prototype.bindEventHandlers = function() {
$('#edit-admin').click(function() {
$('#admins .add-edit-delete-group').hide();
$('#admins .save-cancel-group').show();
$('.filter-admins').prop('disabled', true);
$('#admins .filter-results').css('color', '#AAA');
$('#admins .details').find('input, textarea').prop('readonly', false);
$('#admin-password, #admin-password-confirm').removeClass('required');
$('#admin-notifications').prop('disabled', false);
$('#filter-admins .filter').prop('disabled', true);
$('#filter-admins .results').css('color', '#AAA');
});
/**
@ -138,10 +150,11 @@ AdminsHelper.prototype.bindEventHandlers = function() {
* Cancel add or edit of an admin record.
*/
$('#cancel-admin').click(function() {
var id = $('#admin-id').val();
BackendUsers.helper.resetForm();
if ($('admins .selected-row').length == 0) return;
var id = $('#admins .selected-row').attr('data-id');
BackendUsers.helper.selectRecord(id);
if (id != '') {
BackendUsers.helper.select(id, true);
}
});
};
@ -165,11 +178,9 @@ AdminsHelper.prototype.save = function(admin) {
////////////////////////////////////////////////
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Admin saved successfully!');
BackendUsers.helper.resetForm(true);
// When adding a new record the "admin.id" will be undefined. In this situation
// no record will be selected because we do not yet know the id of the new record,
// but no error will occur either.
BackendUsers.helper.filter($('#admins .filter-key').val(), admin.id);
BackendUsers.helper.resetForm();
$('#filter-admins .key').val('');
BackendUsers.helper.filter('', response.id, true);
}, 'json');
};
@ -189,7 +200,7 @@ AdminsHelper.prototype.delete = function(id) {
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Admin deleted successfully!');
BackendUsers.helper.resetForm();
BackendUsers.helper.filter($('#admins .filter-key').val());
BackendUsers.helper.filter($('#filter-admins .key').val());
});
};
@ -244,29 +255,23 @@ AdminsHelper.prototype.validate = function(admin) {
};
/**
* Resets the admin tab form back to its initial state.
*
* @param {bool} keepRecordData (OPTIONAL = false) If false then the current record data
* will remain on the form.
* Resets the admin form back to its initial state.
*/
AdminsHelper.prototype.resetForm = function(keepRecordData) {
if (keepRecordData == undefined) keepRecordData = false;
AdminsHelper.prototype.resetForm = function() {
$('#admins .add-edit-delete-group').show();
$('#admins .save-cancel-group').hide();
$('#admins .details').find('input, textarea').prop('readonly', true);
$('.filter-admins').prop('disabled', false);
$('#admins .filter-results').css('color', '');
$('#admins .form-message').hide();
$('#admin-notifications').prop('disabled', true);
$('#admins .required').css('border', '');
$('#admin-password, #admin-password-confirm').css('border', '');
$('#admins .details').find('input, textarea').val('');
$('#admin-notifications').removeClass('active');
$('#edit-admin, #delete-admin').prop('disabled', true);
if (!keepRecordData) {
$('#admins .details').find('input, textarea').val('');
$('#admin-notifications').removeClass('active');
$('#edit-admin, #delete-admin').prop('disabled', true);
}
$('#filter-admins .selected-row').removeClass('selected-row');
$('#filter-admins button').prop('disabled', false);
$('#filter-admins .results').css('color', '');
};
/**
@ -296,11 +301,17 @@ AdminsHelper.prototype.display = function(admin) {
};
/**
* Filters admin records depending a string key.
* Filters admin records depending a key string.
*
* @param {string} key This is used to filter the admin records of the database.
* @param {string} key This string is used to filter the admin records of the database.
* @param {numeric} selectId (OPTIONAL = undefined) This record id will be selected when
* the filter operation is finished.
* @param {bool} display (OPTIONAL = false) If true the selected record data are going
* to be displayed on the details column (requires a selected record though).
*/
AdminsHelper.prototype.filter = function(key, selectRecordId) {
AdminsHelper.prototype.filter = function(key, selectId, display) {
if (display == undefined) display = false;
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_admins';
var postData = { 'key': key };
@ -313,19 +324,18 @@ AdminsHelper.prototype.filter = function(key, selectRecordId) {
BackendUsers.helper.filterResults = response;
$('#admins .filter-results').html('');
$('#filter-admins .results').html('');
$.each(response, function(index, admin) {
var html = AdminsHelper.prototype.getFilterHtml(admin);
$('#admins .filter-results').append(html);
$('#filter-admins .results').append(html);
});
if (selectRecordId != undefined) {
$('.admin-row').each(function() {
if ($(this).attr('data-id') == selectRecordId) {
$(this).addClass('selected-row');
return false;
}
});
if (response.length == 0) {
$('#filter-admins .results').html('<em>No results found ...</em>')
}
if (selectId != undefined) {
BackendUsers.helper.select(selectId, display);
}
}, 'json');
};
@ -347,33 +357,34 @@ AdminsHelper.prototype.getFilterHtml = function(admin) {
};
/**
* Select a specific record from the current filter results. If the admin does not exist in
* the list then no record will be selected.
* Select a specific record from the current filter results. If the admin id does not exist
* in the list then no record will be selected.
*
* @param {numeric} id The record id to be selected.
* @param {numeric} id The record id to be selected from the filter results.
* @param {bool} display (OPTIONAL = false) If true then the method will display the record
* on the form.
*
* @task The selected row must always be visible (even if a vertical scroll bar is used to
* navigate through the filter results).
*/
AdminsHelper.prototype.selectRecord = function(id, display) {
AdminsHelper.prototype.select = function(id, display) {
if (display == undefined) display = false;
$('#admins .selected-row').removeClass('selected-row');
$('#filter-admins .selected-row').removeClass('selected-row');
$('.admin-row').each(function() {
if ($(this).attr('data-id') == id) {
$(this).addClass('selected-row');
return;
return false;
}
});
if (display) {
var admin;
$.each(BackendUsers.helper.filterResults, function(index, item) {
if (item.id === id) {
admin = item;
$.each(BackendUsers.helper.filterResults, function(index, admin) {
if (admin.id == id) {
BackendUsers.helper.display(admin);
$('#edit-admin, #delete-admin').prop('disabled', false);
return;
return false;
}
});
}

View file

@ -14,37 +14,48 @@ var ProvidersHelper = function() {
*/
ProvidersHelper.prototype.bindEventHandlers = function() {
/**
* Event: Filter Providers Button "Click"
* Event: Filter Providers Form "Submit"
*
* Filter the provider records with the given key string.
*/
$('.filter-providers').click(function() {
var key = $('#providers .filter-key').val();
$('#filter-providers form').submit(function() {
event.preventDefault();
var key = $('#filter-providers .key').val();
$('.selected-row').removeClass('selected-row');
BackendUsers.helper.resetForm();
BackendUsers.helper.filter(key);
});
/**
* Event: Clear Filter Button "Click"
*/
$('#filter-providers .clear').click(function() {
BackendUsers.helper.filter('');
$('#filter-providers .key').val('');
});
/**
* Event: Filter Provider Row "Click"
*
* Display the selected provider data to the user.
*/
$(document).on('click', '.provider-row', function() {
if ($('#providers .filter-providers').prop('disabled')) {
$('#providers .filter-results').css('color', '#AAA');
return; // exit because we are currently on edit mode
if ($('#filter-providers .filter').prop('disabled')) {
$('#filter-providers .results').css('color', '#AAA');
return; // Exit because we are currently on edit mode.
}
var provider = { 'id': $(this).attr('data-id') };
var providerId = $(this).attr('data-id');
var provider = {};
$.each(BackendUsers.helper.filterResults, function(index, item) {
if (item.id === provider.id) {
if (item.id === providerId) {
provider = item;
return;
return false;
}
});
BackendUsers.helper.display(provider);
$('.selected-row').removeClass('selected-row');
$('#filter-providers .selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#edit-provider, #delete-provider').prop('disabled', false);
});
@ -54,194 +65,19 @@ ProvidersHelper.prototype.bindEventHandlers = function() {
*/
$('#add-provider').click(function() {
BackendUsers.helper.resetForm();
$('#filter-providers button').prop('disabled', true);
$('#filter-providers .results').css('color', '#AAA');
$('#providers .add-edit-delete-group').hide();
$('#providers .save-cancel-group').show();
$('#providers .details').find('input, textarea').prop('readonly', false);
$('#providers .filter-providers').prop('disabled', true);
$('#providers .filter-results').css('color', '#AAA');
$('#provider-password, #provider-password-confirm').addClass('required');
$('#provider-notifications').prop('disabled', false);
$('#provider-services input[type="checkbox"]').prop('disabled', false);
$('#providers .add-break').prop('disabled', false);
$('.edit-break, .delete-break').prop('disabled', false);
$('#providers input[type="checkbox"]').prop('disabled', false);
// Apply default working plan
$.each(GlobalVariables.workingPlan, function(index, workingDay) {
if (workingDay != null) {
$('#' + index).prop('checked', true);
$('#' + index + '-start').val(workingDay.start);
$('#' + index + '-end').val(workingDay.end);
// Add the day's breaks on the breaks table.
$.each(workingDay.breaks, function(i, brk) {
var tr =
'<tr>' +
'<td class="break-day editable">' + GeneralFunctions.ucaseFirstLetter(index) + '</td>' +
'<td class="break-start editable">' + brk.start + '</td>' +
'<td class="break-end editable">' + brk.end + '</td>' +
'<td>' +
'<button type="button" class="btn edit-break" title="Edit Break">' +
'<i class="icon-pencil"></i>' +
'</button>' +
'<button type="button" class="btn delete-break" title="Delete Break">' +
'<i class="icon-remove"></i>' +
'</button>' +
'<button type="button" class="btn save-break hidden" title="Save Break">' +
'<i class="icon-ok"></i>' +
'</button>' +
'<button type="button" class="btn cancel-break hidden" title="Cancel Break">' +
'<i class="icon-ban-circle"></i>' +
'</button>' +
'</td>' +
'</tr>';
$('#breaks').append(tr);
});
} else {
$('#' + index).prop('checked', false);
$('#' + index + '-start').prop('disabled', true);
$('#' + index + '-end').prop('disabled', true);
}
});
// Make break cells editable.
BackendUsers.helper.editableBreakDay($('#breaks .break-day'));
BackendUsers.helper.editableBreakTime($('#breaks').find('.break-start, .break-end'));
// Set timepickers where needed.
$('.working-plan input').timepicker({
'timeFormat': 'HH:mm',
'onSelect': function(datetime, inst) {
// Start time must be earlier than end time.
var start = Date.parse($(this).parent().parent().find('.work-start').val());
var end = Date.parse($(this).parent().parent().find('.work-end').val());
if (start > end) {
$(this).parent().parent().find('.work-end').val(start.addHours(1).toString('HH:mm'));
}
}
});
});
/**
* Event: Day Checkbox "Click"
*
* Enable or disable the time selection for each day.
*/
$('.working-plan input[type="checkbox"]').click(function() {
var id = $(this).attr('id');
if ($(this).prop('checked') == true) {
$('#' + id + '-start').prop('disabled', false).val('09:00');
$('#' + id + '-end').prop('disabled', false).val('18:00');
} else {
$('#' + id + '-start').prop('disabled', true).val('');
$('#' + id + '-end').prop('disabled', true).val('');
}
});
/**
* Event: Add Break Button "Click"
*
* A new row is added on the table and the user can enter the new break
* data. After that he can either press the save or cancel button.
*/
$('.add-break').click(function() {
var tr =
'<tr>' +
'<td class="break-day editable">Monday</td>' +
'<td class="break-start editable">09:00</td>' +
'<td class="break-end editable">10:00</td>' +
'<td>' +
'<button type="button" class="btn edit-break" title="Edit Break">' +
'<i class="icon-pencil"></i>' +
'</button>' +
'<button type="button" class="btn delete-break" title="Delete Break">' +
'<i class="icon-remove"></i>' +
'</button>' +
'<button type="button" class="btn save-break hidden" title="Save Break">' +
'<i class="icon-ok"></i>' +
'</button>' +
'<button type="button" class="btn cancel-break hidden" title="Cancel Break">' +
'<i class="icon-ban-circle"></i>' +
'</button>' +
'</td>' +
'</tr>';
$('#breaks').prepend(tr);
// Bind editable and event handlers.
tr = $('#breaks tr').get()[1];
BackendUsers.helper.editableBreakDay($(tr).find('.break-day'));
BackendUsers.helper.editableBreakTime($(tr).find('.break-start, .break-end'));
$(tr).find('.edit-break').trigger('click');
});
/**
* Event: Edit Break Button "Click"
*
* Enables the row editing for the "Breaks" table rows.
*/
$(document).on('click', '.edit-break', function() {
// Reset previous editable tds
var $previousEdt = $(this).closest('table').find('.editable').get();
$.each($previousEdt, function(index, edt) {
edt.reset();
});
// Make all cells in current row editable.
$(this).parent().parent().children().trigger('edit');
$(this).parent().parent().find('.break-start input, .break-end input').timepicker();
$(this).parent().parent().find('.break-day select').focus();
// Show save - cancel buttons.
$(this).closest('table').find('.edit-break, .delete-break').addClass('hidden');
$(this).parent().find('.save-break, .cancel-break').removeClass('hidden');
});
/**
* Event: Delete Break Button "Click"
*
* Removes the current line from the "Breaks" table.
*/
$(document).on('click', '.delete-break', function() {
$(this).parent().parent().remove();
});
/**
* Event: Cancel Break Button "Click"
*
* Bring the "#breaks" table back to its initial state.
*/
$(document).on('click', '.cancel-break', function() {
BackendUsers.enableCancel = true;
$(this).parent().parent().find('.cancel-editable').trigger('click');
BackendUsers.enableCancel = false;
$(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
$(this).parent().find('.save-break, .cancel-break').addClass('hidden');
});
/**
* Event: Save Break Button "Click"
*
* Save the editable values and restore the table to its initial state.
*/
$(document).on('click', '.save-break', function() {
// Break's start time must always be prior to break's end.
var start = Date.parse($(this).parent().parent().find('.break-start input').val());
var end = Date.parse($(this).parent().parent().find('.break-end input').val());
if (start > end) {
$(this).parent().parent().find('.break-end input').val(start.addHours(1).toString('HH:mm'));
}
BackendUsers.enableSubmit = true;
$(this).parent().parent().find('.editable .submit-editable').trigger('click');
BackendUsers.enableSubmit = false;
$(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
$(this).parent().find('.save-break, .cancel-break').addClass('hidden');
BackendUsers.wp.setup(GlobalVariables.workingPlan);
BackendUsers.wp.timepickers(false);
});
/**
@ -250,16 +86,15 @@ ProvidersHelper.prototype.bindEventHandlers = function() {
$('#edit-provider').click(function() {
$('#providers .add-edit-delete-group').hide();
$('#providers .save-cancel-group').show();
$('.filter-providers').prop('disabled', true);
$('#providers .filter-results').css('color', '#AAA');
$('#filter-providers button').prop('disabled', true);
$('#filter-providers .results').css('color', '#AAA');
$('#providers .details').find('input, textarea').prop('readonly', false);
$('#provider-password, #provider-password-confirm').removeClass('required');
$('#provider-notifications').prop('disabled', false);
$('#provider-services input[type="checkbox"]').prop('disabled', false);
$('#providers .add-break').prop('disabled', false);
$('.edit-break, .delete-break').prop('disabled', false);
$('#providers').find('.add-break, .edit-break, .delete-break').prop('disabled', false);
$('#providers input[type="checkbox"]').prop('disabled', false);
BackendUsers.wp.timepickers(false);
});
/**
@ -299,7 +134,7 @@ ProvidersHelper.prototype.bindEventHandlers = function() {
'notes': $('#provider-notes').val(),
'settings': {
'username': $('#provider-username').val(),
'working_plan': BackendUsers.helper.getWorkingPlan(),
'working_plan': JSON.stringify(BackendUsers.wp.get()),
'notifications': $('#provider-notifications').hasClass('active')
}
};
@ -333,20 +168,11 @@ ProvidersHelper.prototype.bindEventHandlers = function() {
* Cancel add or edit of an provider record.
*/
$('#cancel-provider').click(function() {
var id = $('#filter-providers .selected-row').attr('data-id');
BackendUsers.helper.resetForm();
var provider = { 'id': $('#providers .selected-row').attr('data-id') };
$.each(BackendUsers.helper.filterResults, function(index, item) {
if (item.id === provider.id) {
provider = item;
return;
}
});
BackendUsers.helper.display(provider);
$('#edit-provider, #delete-provider').prop('disabled', false);
if (id != '') {
BackendUsers.helper.select(id, true);
}
});
/**
@ -393,17 +219,16 @@ ProvidersHelper.prototype.save = function(provider) {
///////////////////////////////////////////////////
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Provider saved successfully!');
BackendUsers.helper.resetForm(true);
// If "id" is not defined then no record will be selected (applies when adding
// a new provider record).
BackendUsers.helper.filter($('#providers .filter-key').val(), provider.id);
BackendUsers.helper.resetForm();
$('#filter-providers .key').val('');
BackendUsers.helper.filter('', response.id, true);
}, 'json');
};
/**
* Delete a provider record from database.
*
* @param {int} id Record id to be deleted.
* @param {numeric} id Record id to be deleted.
*/
ProvidersHelper.prototype.delete = function(id) {
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_delete_provider';
@ -416,7 +241,7 @@ ProvidersHelper.prototype.delete = function(id) {
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Provider deleted successfully!');
BackendUsers.helper.resetForm();
BackendUsers.helper.filter($('#providers .filter-key').val());
BackendUsers.helper.filter($('#filter-providers .key').val());
});
};
@ -472,18 +297,15 @@ ProvidersHelper.prototype.validate = function(provider) {
/**
* Resets the admin tab form back to its initial state.
*
* @param {bool} keepRecordData (OPTIONAL = false) If true then the current record data will
* remain on the form.
*/
ProvidersHelper.prototype.resetForm = function(keepRecordData) {
if (keepRecordData == undefined) keepRecordData = false;
ProvidersHelper.prototype.resetForm = function() {
$('#filter-providers .selected-row').removeClass('selected-row');
$('#filter-providers button').prop('disabled', false);
$('#filter-providers .results').css('color', '');
$('#providers .add-edit-delete-group').show();
$('#providers .save-cancel-group').hide();
$('#providers .details').find('input, textarea').prop('readonly', true);
$('.filter-providers').prop('disabled', false);
$('#providers .filter-results').css('color', '');
$('#providers .form-message').hide();
$('#provider-notifications').removeClass('active');
$('#provider-notifications').prop('disabled', true);
@ -491,17 +313,16 @@ ProvidersHelper.prototype.resetForm = function(keepRecordData) {
$('#providers .required').css('border', '');
$('#provider-password, #provider-password-confirm').css('border', '');
$('#providers .add-break').prop('disabled', true);
$('#providers input[type="checkbox"]').prop('disabled', true);
BackendUsers.wp.timepickers(true);
$('#providers .working-plan input[type="text"]').timepicker('destroy');
$('#breaks').find('.edit-break, .delete-break').prop('disabled', true);
if (!keepRecordData) {
$('#edit-provider, #delete-provider').prop('disabled', true);
$('#providers .details').find('input, textarea').val('');
$('#providers input[type="checkbox"]').prop('checked', false);
$('#provider-services input[type="checkbox"]').prop('checked', false);
$('#providers #breaks tbody').empty();
}
$('.breaks').find('.edit-break, .delete-break').prop('disabled', true);
$('#edit-provider, #delete-provider').prop('disabled', true);
$('#providers .details').find('input, textarea').val('');
$('#providers input[type="checkbox"]').prop('checked', false);
$('#provider-services input[type="checkbox"]').prop('checked', false);
$('#providers .breaks tbody').empty();
};
/**
@ -539,74 +360,24 @@ ProvidersHelper.prototype.display = function(provider) {
});
// Display working plan
$('#providers #breaks tbody').empty();
$('#providers .breaks tbody').empty();
var workingPlan = $.parseJSON(provider.settings.working_plan);
$.each(workingPlan, function(index, workingDay) {
if (workingDay != null) {
$('#' + index).prop('checked', true);
$('#' + index + '-start').val(workingDay.start);
$('#' + index + '-end').val(workingDay.end);
// Add the day's breaks on the breaks table.
$.each(workingDay.breaks, function(i, brk) {
var tr =
'<tr>' +
'<td class="break-day editable">' + GeneralFunctions.ucaseFirstLetter(index) + '</td>' +
'<td class="break-start editable">' + brk.start + '</td>' +
'<td class="break-end editable">' + brk.end + '</td>' +
'<td>' +
'<button type="button" class="btn edit-break" title="Edit Break">' +
'<i class="icon-pencil"></i>' +
'</button>' +
'<button type="button" class="btn delete-break" title="Delete Break">' +
'<i class="icon-remove"></i>' +
'</button>' +
'<button type="button" class="btn save-break hidden" title="Save Break">' +
'<i class="icon-ok"></i>' +
'</button>' +
'<button type="button" class="btn cancel-break hidden" title="Cancel Break">' +
'<i class="icon-ban-circle"></i>' +
'</button>' +
'</td>' +
'</tr>';
$('#breaks').append(tr);
});
} else {
$('#' + index).prop('checked', false);
$('#' + index + '-start').prop('disabled', true);
$('#' + index + '-end').prop('disabled', true);
}
});
$('.edit-break, .delete-break').prop('disabled', true);
// Make break cells editable.
BackendUsers.helper.editableBreakDay($('#breaks .break-day'));
BackendUsers.helper.editableBreakTime($('#breaks').find('.break-start, .break-end'));
// Set timepickers where needed.
$('.working-plan input').timepicker({
'timeFormat': 'HH:mm',
'onSelect': function(datetime, inst) {
// Start time must be earlier than end time.
var start = Date.parse($(this).parent().parent().find('.work-start').val());
var end = Date.parse($(this).parent().parent().find('.work-end').val());
if (start > end) {
$(this).parent().parent().find('.work-end').val(start.addHours(1).toString('HH:mm'));
}
}
});
BackendUsers.wp.setup(workingPlan);
$('.breaks').find('.edit-break, .delete-break').prop('disabled', true);
};
/**
* Filters provider records depending a string key.
*
* @param {string} key This is used to filter the provider records of the database.
* @param {numeric} selectRecordId (OPTIONAL) If set, when the function is complete
* @param {numeric} selectId (OPTIONAL = undefined) If set, when the function is complete
* a result row can be set as selected.
* @param {bool} display (OPTIONAL = false) If true then the selected record will be also
* displayed.
*/
ProvidersHelper.prototype.filter = function(key, selectRecordId) {
ProvidersHelper.prototype.filter = function(key, selectId, display) {
if (display == undefined) display = false;
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_providers';
var postData = { 'key': key };
@ -619,19 +390,18 @@ ProvidersHelper.prototype.filter = function(key, selectRecordId) {
BackendUsers.helper.filterResults = response;
$('#providers .filter-results').html('');
$('#filter-providers .results').html('');
$.each(response, function(index, provider) {
var html = ProvidersHelper.prototype.getFilterHtml(provider);
$('#providers .filter-results').append(html);
$('#filter-providers .results').append(html);
});
if (selectRecordId != undefined) {
$('.provider-row').each(function() {
if ($(this).attr('data-id') == selectRecordId) {
$(this).addClass('selected-row');
return false;
}
});
if (response.length == 0) {
$('#filter-providers .results').html('<em>No results found ...</em>')
}
if (selectId != undefined) {
BackendUsers.helper.select(selectId, display);
}
}, 'json');
};
@ -652,44 +422,6 @@ ProvidersHelper.prototype.getFilterHtml = function(provider) {
return html;
};
/**
* Get the current working plan.
*
* @return {string} Returns the working plan (already stringified).
*/
ProvidersHelper.prototype.getWorkingPlan = function() {
var workingPlan = {};
$('.working-plan input[type="checkbox"').each(function() {
var id = $(this).attr('id');
if ($(this).prop('checked') == true) {
workingPlan[id] = {}
workingPlan[id].start = $('#' + id + '-start').val();
workingPlan[id].end = $('#' + id + '-end').val();
workingPlan[id].breaks = [];
$('#breaks tr').each(function(index, tr) {
var day = $(tr).find('.break-day').text().toLowerCase();
if (day == id) {
var start = $(tr).find('.break-start').text();
var end = $(tr).find('.break-end').text();
workingPlan[id].breaks.push({
'start': start,
'end': end
});
}
});
} else {
workingPlan[id] = null;
}
});
return JSON.stringify(workingPlan);
};
/**
* Initialize the editable functionality to the break day table cells.
*
@ -715,7 +447,7 @@ ProvidersHelper.prototype.editableBreakDay = function($selector) {
if (!BackendUsers.enableSubmit) return false; // disable Enter button
}
});
},
};
/**
* Initialize the editable functionality to the break time table cells.
@ -739,4 +471,33 @@ ProvidersHelper.prototype.editableBreakTime = function($selector) {
if (!BackendUsers.enableSubmit) return false; // disable Enter button
}
});
}
};
/**
* Select and display a providers filter result on the form.
*
* @param {numeric} id Record id to be selected.
* @param {bool} display (OPTIONAL = false) If true the record will be displayed on the form.
*/
ProvidersHelper.prototype.select = function(id, display) {
if (display == undefined) display = false;
// Select record in filter results.
$('#filter-providers .provider-row').each(function() {
if ($(this).attr('data-id') == id) {
$(this).addClass('selected-row');
return false;
}
});
// Display record in form (if display = true).
if (display) {
$.each(BackendUsers.helper.filterResults, function(index, provider) {
if (provider.id == id) {
BackendUsers.helper.display(provider);
$('#edit-provider, #delete-provider').prop('disabled', false);
return false;
}
});
}
};

View file

@ -14,37 +14,48 @@ var SecretariesHelper = function() {
*/
SecretariesHelper.prototype.bindEventHandlers = function() {
/**
* Event: Filter Secretaries Button "Click"
* Event: Filter Secretaries Form "Submit"
*
* Filter the secretary records with the given key string.
*/
$('.filter-secretaries').click(function() {
var key = $('#secretaries .filter-key').val();
$('.selected-row').removeClass('selected-row');
$('#filter-secretaries form').submit(function() {
event.preventDefault();
var key = $('#filter-secretaries .key').val();
$('#filter-secretaries .selected-row').removeClass('selected-row');
BackendUsers.helper.resetForm();
BackendUsers.helper.filter(key);
});
/**
* Event: Clear Filter Results Button "Click"
*/
$('#filter-secretaries .clear').click(function() {
BackendUsers.helper.filter('');
$('#filter-secretaries .key').val('');
});
/**
* Event: Filter Secretary Row "Click"
*
* Display the selected secretary data to the user.
*/
$(document).on('click', '.secretary-row', function() {
if ($('#secretaries .filter-secretaries').prop('disabled')) {
$('#secretaries .filter-results').css('color', '#AAA');
if ($('#filter-secretaries .filter').prop('disabled')) {
$('#filter-secretaries .results').css('color', '#AAA');
return; // exit because we are currently on edit mode
}
var secretary = { 'id': $(this).attr('data-id') };
var secretaryId = $(this).attr('data-id');
var secretary = {};
$.each(BackendUsers.helper.filterResults, function(index, item) {
if (item.id === secretary.id) {
if (item.id === secretaryId) {
secretary = item;
return;
return false;
}
});
BackendUsers.helper.display(secretary);
$('.selected-row').removeClass('selected-row');
$('#filter-secretaries .selected-row').removeClass('selected-row');
$(this).addClass('selected-row');
$('#edit-secretary, #delete-secretary').prop('disabled', false);
});
@ -54,11 +65,12 @@ SecretariesHelper.prototype.bindEventHandlers = function() {
*/
$('#add-secretary').click(function() {
BackendUsers.helper.resetForm();
$('#filter-secretaries button').prop('disabled', true);
$('#filter-secretaries .results').css('color', '#AAA');
$('#secretaries .add-edit-delete-group').hide();
$('#secretaries .save-cancel-group').show();
$('#secretaries .details').find('input, textarea').prop('readonly', false);
$('#secretaries .filter-secretaries').prop('disabled', true);
$('#secretaries .filter-results').css('color', '#AAA');
$('#secretary-password, #secretary-password-confirm').addClass('required');
$('#secretary-notifications').prop('disabled', false);
$('#secretary-providers input[type="checkbox"]').prop('disabled', false);
@ -68,10 +80,11 @@ SecretariesHelper.prototype.bindEventHandlers = function() {
* Event: Edit Secretary Button "Click"
*/
$('#edit-secretary').click(function() {
$('#filter-secretaries button').prop('disabled', true);
$('#filter-secretaries .results').css('color', '#AAA');
$('#secretaries .add-edit-delete-group').hide();
$('#secretaries .save-cancel-group').show();
$('.filter-secretaries').prop('disabled', true);
$('#secretaries .filter-results').css('color', '#AAA');
$('#secretaries .details').find('input, textarea').prop('readonly', false);
$('#secretary-password, #secretary-password-confirm').removeClass('required');
$('#secretary-notifications').prop('disabled', false);
@ -82,11 +95,11 @@ SecretariesHelper.prototype.bindEventHandlers = function() {
* Event: Delete Secretary Button "Click"
*/
$('#delete-secretary').click(function() {
var providerId = $('#secretary-id').val();
var secretaryId = $('#secretary-id').val();
var messageBtns = {
'Delete': function() {
BackendUsers.helper.delete(providerId);
BackendUsers.helper.delete(secretaryId);
$('#message_box').dialog('close');
},
'Cancel': function() {
@ -148,7 +161,11 @@ SecretariesHelper.prototype.bindEventHandlers = function() {
* Cancel add or edit of an secretary record.
*/
$('#cancel-secretary').click(function() {
var id = $('#secretary-id').val();
BackendUsers.helper.resetForm();
if (id != '') {
BackendUsers.helper.select(id, true);
}
});
};
@ -173,7 +190,8 @@ SecretariesHelper.prototype.save = function(secretary) {
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Secretary saved successfully!');
BackendUsers.helper.resetForm();
BackendUsers.helper.filter($('#secretaries .filter-key').val());
$('#filter-secretaries .key').val('');
BackendUsers.helper.filter('', response.id, true);
}, 'json');
};
@ -193,7 +211,7 @@ SecretariesHelper.prototype.delete = function(id) {
if (!GeneralFunctions.handleAjaxExceptions(response)) return;
Backend.displayNotification('Secretary deleted successfully!');
BackendUsers.helper.resetForm();
BackendUsers.helper.filter($('#secretaries .filter-key').val());
BackendUsers.helper.filter($('#filter-secretaries .key').val());
});
};
@ -256,8 +274,6 @@ SecretariesHelper.prototype.resetForm = function() {
$('#secretaries .save-cancel-group').hide();
$('#edit-secretary, #delete-secretary').prop('disabled', true);
$('#secretaries .details').find('input, textarea').prop('readonly', true);
$('.filter-secretaries').prop('disabled', false);
$('#secretaries .filter-results').css('color', '');
$('#secretaries .form-message').hide();
$('#secretary-notifications').removeClass('active');
$('#secretary-notifications').prop('disabled', true);
@ -265,6 +281,10 @@ SecretariesHelper.prototype.resetForm = function() {
$('#secretary-providers input[type="checkbox"]').prop('disabled', true);
$('#secretaries .required').css('border', '');
$('#secretary-password, #secretary-password-confirm').css('border', '');
$('#filter-secretaries .selected-row').removeClass('selected-row');
$('#filter-secretaries button').prop('disabled', false);
$('#filter-secretaries .results').css('color', '');
};
/**
@ -306,10 +326,13 @@ SecretariesHelper.prototype.display = function(secretary) {
* Filters secretary records depending a string key.
*
* @param {string} key This is used to filter the secretary records of the database.
* @param {numeric} selectRecordId (OPTIONAL) If provided then the given id will be
* @param {numeric} selectId (OPTIONAL = undefined) If provided then the given id will be
* selected in the filter results (only selected, not displayed).
* @param {bool} display (OPTIONAL = false)
*/
SecretariesHelper.prototype.filter = function(key, selectRecordId) {
SecretariesHelper.prototype.filter = function(key, selectId, display) {
if (display == undefined) display = false;
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_filter_secretaries';
var postData = { 'key': key };
@ -322,19 +345,18 @@ SecretariesHelper.prototype.filter = function(key, selectRecordId) {
BackendUsers.helper.filterResults = response;
$('#secretaries .filter-results').html('');
$('#filter-secretaries .results').html('');
$.each(response, function(index, secretary) {
var html = SecretariesHelper.prototype.getFilterHtml(secretary);
$('#secretaries .filter-results').append(html);
$('#filter-secretaries .results').append(html);
});
if (selectRecordId != undefined) {
$('.secretary-row').each(function() {
if ($(this).attr('data-id') == selectRecordId) {
$(this).addClass('selected-row');
return false;
}
});
if (response.length == 0) {
$('#filter-secretaries .results').html('<em>No results found ...</em>')
}
if (selectId != undefined) {
BackendUsers.helper.select(selectId, display);
}
}, 'json');
};
@ -353,4 +375,38 @@ SecretariesHelper.prototype.getFilterHtml = function(secretary) {
'</div>';
return html;
};
/**
* Select a specific record from the current filter results. If the secretary id does not exist
* in the list then no record will be selected.
*
* @param {numeric} id The record id to be selected from the filter results.
* @param {bool} display (OPTIONAL = false) If true then the method will display the record
* on the form.
*
* @task The selected row must always be visible (even if a vertical scroll bar is used to
* navigate through the filter results).
*/
SecretariesHelper.prototype.select = function(id, display) {
if (display == undefined) display = false;
$('#filter-secretaries .selected-row').removeClass('selected-row');
$('#filter-secretaries .secretary-row').each(function() {
if ($(this).attr('data-id') == id) {
$(this).addClass('selected-row');
return false;
}
});
if (display) {
$.each(BackendUsers.helper.filterResults, function(index, admin) {
if (admin.id == id) {
BackendUsers.helper.display(admin);
$('#edit-secretary, #delete-secretary').prop('disabled', false);
return false;
}
});
}
};

View file

@ -483,7 +483,7 @@ var FrontendBook = {
$.each(GlobalVariables.availableServices, function(index, service) {
if (service.id == $('#select-service').val()) {
selServiceDuration = service.duration;
return; // Stop searching ...
return false; // Stop searching ...
}
});

View file

@ -0,0 +1,313 @@
/**
* Contains the working plan functionality. The working plan DOM elements must be same
* in every page this class is used.
*
* @class WorkingPlan
*/
var WorkingPlan = function() {
/**
* This flag is used when trying to cancel row editing. It is
* true only whenever the user presses the cancel button.
*
* @type {bool}
*/
this.enableCancel = false;
/**
* This flag determines whether the jeditables are allowed to submit. It is
* true only whenever the user presses the save button.
*
* @type {bool}
*/
this.enableSubmit = false;
};
/**
* Setup the dom elements of a given working plan.
*
* @param {object} workingPlan Contains the working hours and breaks for each day of the week.
*/
WorkingPlan.prototype.setup = function(workingPlan) {
$.each(workingPlan, function(index, workingDay) {
if (workingDay != null) {
$('#' + index).prop('checked', true);
$('#' + index + '-start').val(workingDay.start);
$('#' + index + '-end').val(workingDay.end);
// Add the day's breaks on the breaks table.
$.each(workingDay.breaks, function(i, brk) {
var tr =
'<tr>' +
'<td class="break-day editable">' + GeneralFunctions.ucaseFirstLetter(index) + '</td>' +
'<td class="break-start editable">' + brk.start + '</td>' +
'<td class="break-end editable">' + brk.end + '</td>' +
'<td>' +
'<button type="button" class="btn edit-break" title="Edit Break">' +
'<i class="icon-pencil"></i>' +
'</button>' +
'<button type="button" class="btn delete-break" title="Delete Break">' +
'<i class="icon-remove"></i>' +
'</button>' +
'<button type="button" class="btn save-break hidden" title="Save Break">' +
'<i class="icon-ok"></i>' +
'</button>' +
'<button type="button" class="btn cancel-break hidden" title="Cancel Break">' +
'<i class="icon-ban-circle"></i>' +
'</button>' +
'</td>' +
'</tr>';
$('.breaks').append(tr);
});
} else {
$('#' + index).prop('checked', false);
$('#' + index + '-start').prop('disabled', true);
$('#' + index + '-end').prop('disabled', true);
}
});
// Make break cells editable.
WorkingPlan.prototype.editableBreakDay($('.breaks .break-day'));
WorkingPlan.prototype.editableBreakTime($('.breaks').find('.break-start, .break-end'));
};
/**
* This method makes editable the break day cells.
*
* @param {object} $selector The jquery selector ready for use.
*/
WorkingPlan.prototype.editableBreakDay = function($selector) {
$selector.editable(function(value, settings) {
return value;
}, {
'type': 'select',
'data': '{ "Monday": "Monday", "Tuesday": "Tuesday", "Wednesday": "Wednesday", '
+ '"Thursday": "Thursday", "Friday": "Friday", "Saturday": "Saturday", '
+ '"Sunday": "Sunday", "selected": "Monday"}',
'event': 'edit',
'height': '30px',
'submit': '<button type="button" class="hidden submit-editable">Submit</button>',
'cancel': '<button type="button" class="hidden cancel-editable">Cancel</button>',
'onblur': 'ignore',
'onreset': function(settings, td) {
if (!WorkingPlan.prototype.enableCancel) return false; // disable ESC button
},
'onsubmit': function(settings, td) {
if (!WorkingPlan.prototype.enableSubmit) return false; // disable Enter button
}
});
};
/**
* This method makes editable the break time cells.
*
* @param {object} $selector The jquery selector ready for use.
*/
WorkingPlan.prototype.editableBreakTime = function($selector) {
$selector.editable(function(value, settings) {
// Do not return the value because the user needs to press the "Save" button.
return value;
}, {
'event': 'edit',
'height': '25px',
'submit': '<button type="button" class="hidden submit-editable">Submit</button>',
'cancel': '<button type="button" class="hidden cancel-editable">Cancel</button>',
'onblur': 'ignore',
'onreset': function(settings, td) {
if (!WorkingPlan.prototype.enableCancel) return false; // disable ESC button
},
'onsubmit': function(settings, td) {
if (!WorkingPlan.prototype.enableSubmit) return false; // disable Enter button
}
});
};
/**
* Binds the event handlers for the working plan dom elements.
*/
WorkingPlan.prototype.bindEventHandlers = function() {
/**
* Event: Day Checkbox "Click"
*
* Enable or disable the time selection for each day.
*/
$('.working-plan input[type="checkbox"]').click(function() {
var id = $(this).attr('id');
if ($(this).prop('checked') == true) {
$('#' + id + '-start').prop('disabled', false).val('09:00');
$('#' + id + '-end').prop('disabled', false).val('18:00');
} else {
$('#' + id + '-start').prop('disabled', true).val('');
$('#' + id + '-end').prop('disabled', true).val('');
}
});
/**
* Event: Add Break Button "Click"
*
* A new row is added on the table and the user can enter the new break
* data. After that he can either press the save or cancel button.
*/
$('.add-break').click(function() {
var tr =
'<tr>' +
'<td class="break-day editable">Monday</td>' +
'<td class="break-start editable">09:00</td>' +
'<td class="break-end editable">10:00</td>' +
'<td>' +
'<button type="button" class="btn edit-break" title="Edit Break">' +
'<i class="icon-pencil"></i>' +
'</button>' +
'<button type="button" class="btn delete-break" title="Delete Break">' +
'<i class="icon-remove"></i>' +
'</button>' +
'<button type="button" class="btn save-break hidden" title="Save Break">' +
'<i class="icon-ok"></i>' +
'</button>' +
'<button type="button" class="btn cancel-break hidden" title="Cancel Break">' +
'<i class="icon-ban-circle"></i>' +
'</button>' +
'</td>' +
'</tr>';
$('.breaks').prepend(tr);
// Bind editable and event handlers.
tr = $('.breaks tr').get()[1];
BackendSettings.editableBreakDay($(tr).find('.break-day'));
BackendSettings.editableBreakTime($(tr).find('.break-start, .break-end'));
$(tr).find('.edit-break').trigger('click');
});
/**
* Event: Edit Break Button "Click"
*
* Enables the row editing for the "Breaks" table rows.
*/
$(document).on('click', '.edit-break', function() {
// Reset previous editable tds
var $previousEdt = $(this).closest('table').find('.editable').get();
$.each($previousEdt, function(index, edt) {
edt.reset();
});
// Make all cells in current row editable.
$(this).parent().parent().children().trigger('edit');
$(this).parent().parent().find('.break-start input, .break-end input').timepicker();
$(this).parent().parent().find('.break-day select').focus();
// Show save - cancel buttons.
$(this).closest('table').find('.edit-break, .delete-break').addClass('hidden');
$(this).parent().find('.save-break, .cancel-break').removeClass('hidden');
});
/**
* Event: Delete Break Button "Click"
*
* Removes the current line from the "Breaks" table.
*/
$(document).on('click', '.delete-break', function() {
$(this).parent().parent().remove();
});
/**
* Event: Cancel Break Button "Click"
*
* Bring the ".breaks" table back to its initial state.
*/
$(document).on('click', '.cancel-break', function() {
WorkingPlan.prototype.enableCancel = true;
$(this).parent().parent().find('.cancel-editable').trigger('click');
WorkingPlan.prototype.enableCancel = false;
$(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
$(this).parent().find('.save-break, .cancel-break').addClass('hidden');
});
/**
* Event: Save Break Button "Click"
*
* Save the editable values and restore the table to its initial state.
*/
$(document).on('click', '.save-break', function() {
// Break's start time must always be prior to break's end.
var start = Date.parse($(this).parent().parent().find('.break-start input').val());
var end = Date.parse($(this).parent().parent().find('.break-end input').val());
if (start > end) {
$(this).parent().parent().find('.break-end input').val(start.addHours(1).toString('HH:mm'));
}
WorkingPlan.prototype.enableSubmit = true;
$(this).parent().parent().find('.editable .submit-editable').trigger('click');
WorkingPlan.prototype.enableSubmit = false;
$(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
$(this).parent().find('.save-break, .cancel-break').addClass('hidden');
});
};
/**
* Get the working plan settings.
*
* @returns {object} Returns the working plan settings object.
*/
WorkingPlan.prototype.get = function() {
var workingPlan = {};
$('.working-plan input[type="checkbox"').each(function() {
var id = $(this).attr('id');
if ($(this).prop('checked') == true) {
workingPlan[id] = {}
workingPlan[id].start = $('#' + id + '-start').val();
workingPlan[id].end = $('#' + id + '-end').val();
workingPlan[id].breaks = [];
$('.breaks tr').each(function(index, tr) {
var day = $(tr).find('.break-day').text().toLowerCase();
if (day == id) {
var start = $(tr).find('.break-start').text();
var end = $(tr).find('.break-end').text();
workingPlan[id].breaks.push({
'start': start,
'end': end
});
}
});
} else {
workingPlan[id] = null;
}
});
return workingPlan;
};
/**
* Enables or disabled the timepicker functionality from the working plan input
* text fields.
*
* @param {bool} disabled (OPTIONAL = false) If true then the timepickers will be
* disabled.
*/
WorkingPlan.prototype.timepickers = function(disabled) {
if (disabled == undefined) disabled == false;
if (disabled == false) {
// Set timepickers where needed.
$('.working-plan input').timepicker({
'timeFormat': 'HH:mm',
'onSelect': function(datetime, inst) {
// Start time must be earlier than end time.
var start = Date.parse($(this).parent().parent().find('.work-start').val());
var end = Date.parse($(this).parent().parent().find('.work-end').val());
if (start > end) {
$(this).parent().parent().find('.work-end').val(start.addHours(1).toString('HH:mm'));
}
}
});
} else {
$('.working-plan input').timepicker('destroy');
}
};