diff --git a/application/controllers/Services.php b/application/controllers/Services.php
index f18636f9..b02f7248 100644
--- a/application/controllers/Services.php
+++ b/application/controllers/Services.php
@@ -36,7 +36,7 @@ class Services extends EA_Controller {
/**
* Render the backend services page.
*
- * On this page admin users will be able to manage services, which are eventually selected by customers during the
+ * On this page admin users will be able to manage services, which are eventually selected by customers during the
* booking process.
*/
public function index()
@@ -51,7 +51,12 @@ class Services extends EA_Controller {
$user_id = session('user_id');
$role_slug = session('role_slug');
-
+
+ script_vars([
+ 'user_id' => $user_id,
+ 'role_slug' => $role_slug,
+ ]);
+
html_vars([
'page_title' => lang('services'),
'active_menu' => PRIV_SERVICES,
@@ -80,7 +85,7 @@ class Services extends EA_Controller {
$order_by = 'name ASC';
$limit = request('limit', 1000);
-
+
$offset = 0;
$services = $this->services_model->search($keyword, $limit, $offset, $order_by);
@@ -100,7 +105,9 @@ class Services extends EA_Controller {
{
try
{
- $service = json_decode(request('service'), TRUE);
+ $service = request('service');
+
+ $service['id_categories'] = $service['id_categories'] ?: null;
if (cannot('add', PRIV_SERVICES))
{
@@ -127,13 +134,15 @@ class Services extends EA_Controller {
{
try
{
- $service = json_decode(request('service'), TRUE);
+ $service = request('service');
+
+ $service['id_categories'] = $service['id_categories'] ?: null;
if (cannot('edit', PRIV_SERVICES))
{
abort(403, 'Forbidden');
}
-
+
$service_id = $this->services_model->save($service);
json_response([
diff --git a/application/views/pages/services.php b/application/views/pages/services.php
index 409f662a..796cb491 100755
--- a/application/views/pages/services.php
+++ b/application/views/pages/services.php
@@ -146,27 +146,11 @@
-
-
-
+
+
+
+
+
+
diff --git a/assets/js/http/services_http_client.js b/assets/js/http/services_http_client.js
index edc118b0..e44c2452 100644
--- a/assets/js/http/services_http_client.js
+++ b/assets/js/http/services_http_client.js
@@ -10,6 +10,17 @@
* ---------------------------------------------------------------------------- */
App.Http.Services = (function () {
+ /**
+ * Save (create or update) a service.
+ *
+ * @param {Object} service
+ *
+ * @return {Object}
+ */
+ function save(service) {
+ return service.id ? update(service) : create(service);
+ }
+
/**
* Create an service.
*
@@ -107,6 +118,7 @@ App.Http.Services = (function () {
}
return {
+ save,
create,
update,
destroy,
diff --git a/assets/js/pages/backend_services.js b/assets/js/pages/backend_services.js
deleted file mode 100644
index 89d9d415..00000000
--- a/assets/js/pages/backend_services.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* ----------------------------------------------------------------------------
- * Easy!Appointments - Open Source Web Scheduler
- *
- * @package EasyAppointments
- * @author A.Tselegidis
- * @copyright Copyright (c) Alex Tselegidis
- * @license https://opensource.org/licenses/GPL-3.0 - GPLv3
- * @link https://easyappointments.org
- * @since v1.0.0
- * ---------------------------------------------------------------------------- */
-
-window.BackendServices = window.BackendServices || {};
-
-/**
- * Backend Services
- *
- * This namespace handles the js functionality of the backend services page.
- *
- * @module BackendServices
- */
-(function (exports) {
- 'use strict';
-
- /**
- * Contains the basic record methods for the page.
- *
- * @type {ServicesHelper}
- */
- var helper;
-
- var servicesHelper = new ServicesHelper();
-
- /**
- * Default initialize method of the page.
- *
- * @param {Boolean} [defaultEventHandlers] Optional (true), determines whether to bind the default event handlers.
- */
- exports.initialize = function (defaultEventHandlers) {
- defaultEventHandlers = defaultEventHandlers || true;
-
- // Instantiate helper object (service helper by default).
- helper = servicesHelper;
- helper.resetForm();
- helper.filter('');
- helper.bindEventHandlers();
-
- if (defaultEventHandlers) {
- bindEventHandlers();
- }
-
- BackendServices.updateAvailableCategories();
-
- Backend.placeFooterToBottom();
- };
-
- /**
- * Binds the default event handlers of the backend services page.
- *
- * Do not use this method if you include the "BackendServices" namespace on another page.
- */
- function bindEventHandlers() {
- //
- }
-
- /**
- * Update the service category list box.
- *
- * Use this method every time a change is made to the service categories db table.
- */
- exports.updateAvailableCategories = function () {
- var url = GlobalVariables.baseUrl + '/index.php/categories/search';
-
- var data = {
- csrf_token: GlobalVariables.csrfToken,
- key: ''
- };
-
- $.post(url, data).done(function (response) {
- GlobalVariables.categories = response;
- var $select = $('#service-category');
-
- $select.empty();
-
- response.forEach(function (category) {
- $select.append(new Option(category.name, category.id));
- });
-
- $select.append(new Option('- ' + App.Lang.no_category + ' -', null)).val('null');
- });
- };
-})(window.BackendServices);
diff --git a/assets/js/pages/backend_services_helper.js b/assets/js/pages/services.js
similarity index 57%
rename from assets/js/pages/backend_services_helper.js
rename to assets/js/pages/services.js
index dd5f046a..c8a7a4ef 100644
--- a/assets/js/pages/backend_services_helper.js
+++ b/assets/js/pages/services.js
@@ -9,35 +9,23 @@
* @since v1.0.0
* ---------------------------------------------------------------------------- */
-(function () {
- 'use strict';
-
- /**
- * ServicesHelper
- *
- * This class contains the methods that will be used by the "Services" tab of the page.
- *
- * @class ServicesHelper
- */
- function ServicesHelper() {
- this.filterResults = {};
- this.filterLimit = 20;
- }
-
- ServicesHelper.prototype.bindEventHandlers = function () {
- var instance = this;
+App.Pages.Services = (function () {
+ const $services = $('#services');
+ let filterResults = {};
+ let filterLimit = 20;
+ function bindEventHandlers() {
/**
* Event: Filter Services Form "Submit"
*
* @param {jQuery.Event} event
*/
- $('#services').on('submit', '#filter-services form', function (event) {
+ $services.on('submit', '#filter-services form', function (event) {
event.preventDefault();
- var key = $('#filter-services .key').val();
+ const key = $('#filter-services .key').val();
$('#filter-services .selected').removeClass('selected');
- instance.resetForm();
- instance.filter(key);
+ resetForm();
+ filter(key);
});
/**
@@ -45,21 +33,22 @@
*
* Display the selected service data to the user.
*/
- $('#services').on('click', '.service-row', function () {
+ $services.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');
+ const serviceId = $(this).attr('data-id');
- var service = instance.filterResults.find(function (filterResult) {
+ const service = filterResults.find(function (filterResult) {
return Number(filterResult.id) === Number(serviceId);
});
// Add dedicated provider link.
- var dedicatedUrl = GlobalVariables.baseUrl + '/index.php?service=' + encodeURIComponent(service.id);
- var $link = $('', {
+ const dedicatedUrl = App.Utils.Url.siteUrl('?service=' + encodeURIComponent(service.id));
+
+ const $link = $('', {
'href': dedicatedUrl,
'html': [
$('', {
@@ -70,7 +59,7 @@
$('#services .record-details h3').find('a').remove().end().append($link);
- instance.display(service);
+ display(service);
$('#filter-services .selected').removeClass('selected');
$(this).addClass('selected');
$('#edit-service, #delete-service').prop('disabled', false);
@@ -79,8 +68,8 @@
/**
* Event: Add New Service Button "Click"
*/
- $('#services').on('click', '#add-service', function () {
- instance.resetForm();
+ $services.on('click', '#add-service', function () {
+ resetForm();
$('#services .add-edit-delete-group').hide();
$('#services .save-cancel-group').show();
$('#services .record-details').find('input, select, textarea').prop('disabled', false);
@@ -92,7 +81,7 @@
$('#service-duration').val('30');
$('#service-price').val('0');
$('#service-currency').val('');
- $('#service-category').val('null');
+ $('#service-category').val('');
$('#service-availabilities-type').val('flexible');
$('#service-attendants-number').val('1');
});
@@ -102,19 +91,19 @@
*
* Cancel add or edit of a service record.
*/
- $('#services').on('click', '#cancel-service', function () {
- var id = $('#service-id').val();
- instance.resetForm();
+ $services.on('click', '#cancel-service', function () {
+ const id = $('#service-id').val();
+ resetForm();
if (id !== '') {
- instance.select(id, true);
+ select(id, true);
}
});
/**
* Event: Save Service Button "Click"
*/
- $('#services').on('click', '#save-service', function () {
- var service = {
+ $services.on('click', '#save-service', function () {
+ const service = {
name: $('#service-name').val(),
duration: $('#service-duration').val(),
price: $('#service-price').val(),
@@ -122,30 +111,25 @@
description: $('#service-description').val(),
location: $('#service-location').val(),
availabilities_type: $('#service-availabilities-type').val(),
- attendants_number: $('#service-attendants-number').val()
+ attendants_number: $('#service-attendants-number').val(),
+ id_categories: $('#service-category').val() || null
};
- if ($('#service-category').val() !== 'null') {
- service.id_categories = $('#service-category').val();
- } else {
- service.id_categories = null;
- }
-
if ($('#service-id').val() !== '') {
service.id = $('#service-id').val();
}
- if (!instance.validate()) {
+ if (!validate()) {
return;
}
- instance.save(service);
+ save(service);
});
/**
* Event: Edit Service Button "Click"
*/
- $('#services').on('click', '#edit-service', function () {
+ $services.on('click', '#edit-service', function () {
$('#services .add-edit-delete-group').hide();
$('#services .save-cancel-group').show();
$('#services .record-details').find('input, select, textarea').prop('disabled', false);
@@ -156,9 +140,9 @@
/**
* Event: Delete Service Button "Click"
*/
- $('#services').on('click', '#delete-service', function () {
- var serviceId = $('#service-id').val();
- var buttons = [
+ $services.on('click', '#delete-service', function () {
+ const serviceId = $('#service-id').val();
+ const buttons = [
{
text: App.Lang.cancel,
click: function () {
@@ -168,30 +152,15 @@
{
text: App.Lang.delete,
click: function () {
- instance.delete(serviceId);
+ remove(serviceId);
$('#message-box').dialog('close');
}
}
];
- GeneralFunctions.displayMessageBox(App.Lang.delete_service, App.Lang.delete_record_prompt, buttons);
+ App.Utils.Message.show(App.Lang.delete_service, App.Lang.delete_record_prompt, buttons);
});
- };
-
- /**
- * Remove the previously registered event handlers.
- */
- ServicesHelper.prototype.unbindEventHandlers = function () {
- $('#services')
- .off('submit', '#filter-services form')
- .off('click', '#filter-services .clear')
- .off('click', '.service-row')
- .off('click', '#add-service')
- .off('click', '#cancel-service')
- .off('click', '#save-service')
- .off('click', '#edit-service')
- .off('click', '#delete-service');
- };
+ }
/**
* Save service record to database.
@@ -199,59 +168,40 @@
* @param {Object} service Contains the service record data. If an 'id' value is provided
* then the update operation is going to be executed.
*/
- ServicesHelper.prototype.save = function (service) {
- var url = GlobalVariables.baseUrl + '/index.php/services/' + (service.id ? 'update' : 'create');
-
- var data = {
- csrf_token: GlobalVariables.csrfToken,
- service: JSON.stringify(service)
- };
-
- $.post(url, data).done(
- function (response) {
- Backend.displayNotification(App.Lang.service_saved);
- this.resetForm();
- $('#filter-services .key').val('');
- this.filter('', response.id, true);
- }.bind(this)
- );
- };
+ function save(service) {
+ App.Http.Services.save(service).then((response) => {
+ Backend.displayNotification(App.Lang.service_saved);
+ resetForm();
+ $('#filter-services .key').val('');
+ filter('', response.id, true);
+ });
+ }
/**
* Delete a service record from database.
*
* @param {Number} id Record ID to be deleted.
*/
- ServicesHelper.prototype.delete = function (id) {
- var url = GlobalVariables.baseUrl + '/index.php/services/destroy';
-
- var data = {
- csrf_token: GlobalVariables.csrfToken,
- service_id: id
- };
-
- $.post(url, data).done(
- function () {
- Backend.displayNotification(App.Lang.service_deleted);
-
- this.resetForm();
- this.filter($('#filter-services .key').val());
- }.bind(this)
- );
- };
+ function remove(id) {
+ App.Http.Services.destroy(id).then(() => {
+ Backend.displayNotification(App.Lang.service_deleted);
+ resetForm();
+ filter($('#filter-services .key').val());
+ });
+ }
/**
* Validates a service record.
*
* @return {Boolean} Returns the validation result.
*/
- ServicesHelper.prototype.validate = function () {
+ function validate() {
$('#services .is-invalid').removeClass('is-invalid');
$('#services .form-message').removeClass('alert-danger').hide();
try {
// Validate required fields.
- var missingRequired = false;
+ let missingRequired = false;
$('#services .required').each(function (index, requiredField) {
if (!$(requiredField).val()) {
@@ -275,12 +225,12 @@
$('#services .form-message').addClass('alert-danger').text(error.message).show();
return false;
}
- };
+ }
/**
* Resets the service tab form back to its initial state.
*/
- ServicesHelper.prototype.resetForm = function () {
+ function resetForm() {
$('#filter-services .selected').removeClass('selected');
$('#filter-services button').prop('disabled', false);
$('#filter-services .results').css('color', '');
@@ -294,14 +244,14 @@
$('#services .record-details .is-invalid').removeClass('is-invalid');
$('#services .record-details .form-message').hide();
- };
+ }
/**
* Display a service record into the service form.
*
* @param {Object} service Contains the service record data.
*/
- ServicesHelper.prototype.display = function (service) {
+ function display(service) {
$('#service-id').val(service.id);
$('#service-name').val(service.name);
$('#service-duration').val(service.duration);
@@ -312,9 +262,9 @@
$('#service-availabilities-type').val(service.availabilities_type);
$('#service-attendants-number').val(service.attendants_number);
- var categoryId = service.id_categories !== null ? service.id_categories : 'null';
+ const categoryId = service.id_categories !== null ? service.id_categories : '';
$('#service-category').val(categoryId);
- };
+ }
/**
* Filters service records depending a string keyword.
@@ -322,55 +272,41 @@
* @param {String} keyword This is used to filter the service records of the database.
* @param {Number} selectId Optional, if set then after the filter operation the record with this
* ID will be selected (but not displayed).
- * @param {Boolean} display Optional (false), if true then the selected record will be displayed on the form.
+ * @param {Boolean} show Optional (false), if true then the selected record will be displayed on the form.
*/
- ServicesHelper.prototype.filter = function (keyword, selectId, display) {
- display = display || false;
+ function filter(keyword, selectId = null, show = false) {
+ App.Http.Services.search(keyword, filterLimit).then((response) => {
+ filterResults = response;
- var url = GlobalVariables.baseUrl + '/index.php/services/search';
+ $('#filter-services .results').empty();
- var data = {
- csrf_token: GlobalVariables.csrfToken,
- keyword: keyword,
- limit: this.filterLimit
- };
+ response.forEach(function (service, index) {
+ $('#filter-services .results').append(getFilterHtml(service)).append($('
'));
+ });
- $.post(url, data).done(
- function (response) {
- this.filterResults = response;
+ if (response.length === 0) {
+ $('#filter-services .results').append(
+ $('', {
+ 'text': App.Lang.no_records_found
+ })
+ );
+ } else if (response.length === filterLimit) {
+ $('', {
+ 'type': 'button',
+ 'class': 'btn btn-outline-secondary w-100 load-more text-center',
+ 'text': App.Lang.load_more,
+ 'click': function () {
+ filterLimit += 20;
+ filter(keyword, selectId, show);
+ }.bind(this)
+ }).appendTo('#filter-services .results');
+ }
- $('#filter-services .results').empty();
-
- response.forEach(function (service, index) {
- $('#filter-services .results')
- .append(ServicesHelper.prototype.getFilterHtml(service))
- .append($('
'));
- });
-
- if (response.length === 0) {
- $('#filter-services .results').append(
- $('', {
- 'text': App.Lang.no_records_found
- })
- );
- } else if (response.length === this.filterLimit) {
- $('', {
- 'type': 'button',
- 'class': 'btn btn-outline-secondary w-100 load-more text-center',
- 'text': App.Lang.load_more,
- 'click': function () {
- this.filterLimit += 20;
- this.filter(keyword, selectId, display);
- }.bind(this)
- }).appendTo('#filter-services .results');
- }
-
- if (selectId) {
- this.select(selectId, display);
- }
- }.bind(this)
- );
- };
+ if (selectId) {
+ select(selectId, show);
+ }
+ });
+ }
/**
* Get Filter HTML
@@ -381,10 +317,10 @@
*
* @return {String} The HTML code that represents the record on the filter results list.
*/
- ServicesHelper.prototype.getFilterHtml = function (service) {
- var name = service.name;
+ function getFilterHtml(service) {
+ const name = service.name;
- var info = service.duration + ' min - ' + service.price + ' ' + service.currency;
+ const info = service.duration + ' min - ' + service.price + ' ' + service.currency;
return $('', {
'class': 'service-row entry',
@@ -400,32 +336,65 @@
$('
')
]
});
- };
+ }
/**
* 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 {Number} id The record id to be selected from the filter results.
- * @param {Boolean} display Optional (false), if true then the method will display the record on the form.
+ * @param {Boolean} show Optional (false), if true then the method will display the record on the form.
*/
- ServicesHelper.prototype.select = function (id, display) {
- display = display || false;
-
+ function select(id, show = false) {
$('#filter-services .selected').removeClass('selected');
$('#filter-services .service-row[data-id="' + id + '"]').addClass('selected');
- if (display) {
- var service = this.filterResults.find(function (filterResult) {
+ if (show) {
+ const service = filterResults.find(function (filterResult) {
return Number(filterResult.id) === Number(id);
});
- this.display(service);
+ display(service);
$('#edit-service, #delete-service').prop('disabled', false);
}
- };
+ }
- window.ServicesHelper = ServicesHelper;
+ /**
+ * Update the service category list box.
+ *
+ * Use this method every time a change is made to the service categories db table.
+ */
+ function updateAvailableCategories() {
+ App.Http.Categories.search('', 999).then((response) => {
+ var $select = $('#service-category');
+
+ $select.empty();
+
+ response.forEach(function (category) {
+ $select.append(new Option(category.name, category.id));
+ });
+
+ $select.append(new Option('- ' + App.Lang.no_category + ' -', '')).val('');
+ });
+ }
+
+ function init() {
+ resetForm();
+ filter('');
+ bindEventHandlers();
+ updateAvailableCategories();
+ }
+
+ document.addEventListener('DOMContentLoaded', init);
+
+ return {
+ filter,
+ save,
+ remove,
+ getFilterHtml,
+ resetForm,
+ select
+ };
})();