File: //proc/self/cwd/nueva/admin346k89tef/themes/new-theme/js/pages/product/edit/combinations-manager.js
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import ProductMap from '@pages/product/product-map';
import CombinationsGridRenderer from '@pages/product/edit/combinations-grid-renderer';
import CombinationsService from '@pages/product/services/combinations-service';
import DynamicPaginator from '@components/pagination/dynamic-paginator';
import SubmittableInput from '@components/form/submittable-input';
import ProductEventMap from '@pages/product/product-event-map';
import initCombinationModal from '@pages/product/components/combination-modal';
import initFilters from '@pages/product/components/filters';
import ConfirmModal from '@components/modal';
import initCombinationGenerator from '@pages/product/components/generator';
import {getProductAttributeGroups} from '@pages/product/services/attribute-groups';
const {$} = window;
const CombinationEvents = ProductEventMap.combinations;
const CombinationsMap = ProductMap.combinations;
export default class CombinationsManager {
/**
* @param {int} productId
* @returns {{}}
*/
constructor(productId) {
this.productId = productId;
this.eventEmitter = window.prestashop.instance.eventEmitter;
this.$productForm = $(ProductMap.productForm);
this.$combinationsContainer = $(
ProductMap.combinations.combinationsContainer,
);
this.combinationIdInputsSelector = ProductMap.combinations.combinationIdInputsSelector;
this.$externalCombinationTab = $(
ProductMap.combinations.externalCombinationTab,
);
this.$preloader = $(ProductMap.combinations.preloader);
this.$paginatedList = $(CombinationsMap.combinationsPaginatedList);
this.$emptyState = $(CombinationsMap.emptyState);
this.paginator = null;
this.combinationsRenderer = null;
this.filtersApp = null;
this.combinationModalApp = null;
this.combinationGeneratorApp = null;
this.initialized = false;
this.combinationsService = new CombinationsService(this.productId);
this.productAttributeGroups = [];
this.init();
return {};
}
/**
* @private
*/
init() {
// Paginate to first page when tab is shown
this.$productForm
.find(CombinationsMap.navigationTab)
.on('shown.bs.tab', () => this.showCombinationTab());
this.$productForm
.find(CombinationsMap.navigationTab)
.on('hidden.bs.tab', () => this.hideCombinationTab());
// Finally watch events related to combination listing
this.watchEvents();
}
/**
* @private
*/
showCombinationTab() {
this.$externalCombinationTab.removeClass('d-none');
this.firstInit();
}
/**
* @private
*/
hideCombinationTab() {
this.$externalCombinationTab.addClass('d-none');
}
/**
* @private
*/
firstInit() {
if (this.initialized) {
return;
}
this.initialized = true;
this.combinationGeneratorApp = initCombinationGenerator(
CombinationsMap.combinationsGeneratorContainer,
this.eventEmitter,
this.productId,
);
this.combinationModalApp = initCombinationModal(
CombinationsMap.editModal,
this.productId,
this.eventEmitter,
);
this.filtersApp = initFilters(
CombinationsMap.combinationsFiltersContainer,
this.eventEmitter,
this.productAttributeGroups,
);
this.initPaginatedList();
this.refreshCombinationList(true);
}
/**
* @param {boolean} firstTime
* @returns {Promise<void>}
*
* @private
*/
async refreshCombinationList(firstTime) {
// Preloader is only shown on first load
this.$preloader.toggleClass('d-none', !firstTime);
this.$paginatedList.toggleClass('d-none', firstTime);
this.$emptyState.addClass('d-none');
// When attributes are refreshed we show first page
this.paginator.paginate(1);
// Wait for product attributes to adapt rendering depending on their number
this.productAttributeGroups = await getProductAttributeGroups(
this.productId,
);
this.filtersApp.filters = this.productAttributeGroups;
this.eventEmitter.emit(CombinationEvents.clearFilters);
this.$preloader.addClass('d-none');
const hasCombinations = this.productAttributeGroups && this.productAttributeGroups.length;
this.$paginatedList.toggleClass('d-none', !hasCombinations);
if (!hasCombinations) {
// Empty list
this.combinationsRenderer.render({combinations: []});
this.$emptyState.removeClass('d-none');
}
}
/**
* @private
*/
refreshPage() {
this.paginator.paginate(this.paginator.getCurrentPage());
}
/**
* @private
*/
initPaginatedList() {
this.combinationsRenderer = new CombinationsGridRenderer();
this.paginator = new DynamicPaginator(
CombinationsMap.paginationContainer,
this.combinationsService,
this.combinationsRenderer,
);
this.initSubmittableInputs();
this.$combinationsContainer.on(
'change',
CombinationsMap.isDefaultInputsSelector,
async (e) => {
if (!e.currentTarget.checked) {
return;
}
await this.updateDefaultCombination(e.currentTarget);
},
);
this.$combinationsContainer.on(
'click',
CombinationsMap.removeCombinationSelector,
async (e) => {
await this.removeCombination(e.currentTarget);
},
);
this.initSortingColumns();
this.paginator.paginate(1);
}
/**
* @private
*/
watchEvents() {
/* eslint-disable */
this.eventEmitter.on(CombinationEvents.refreshCombinationList, () =>
this.refreshCombinationList(false)
);
this.eventEmitter.on(CombinationEvents.refreshPage, () =>
this.refreshPage()
);
/* eslint-disable */
this.eventEmitter.on(
CombinationEvents.updateAttributeGroups,
attributeGroups => {
const currentFilters = this.combinationsService.getFilters();
currentFilters.attributes = {};
Object.keys(attributeGroups).forEach(attributeGroupId => {
currentFilters.attributes[attributeGroupId] = [];
const attributes = attributeGroups[attributeGroupId];
attributes.forEach(attribute => {
currentFilters.attributes[attributeGroupId].push(attribute.id);
});
});
this.combinationsService.setFilters(currentFilters);
this.paginator.paginate(1);
}
);
this.eventEmitter.on(CombinationEvents.combinationGeneratorReady, () => {
const $generateButtons = $(
ProductMap.combinations.generateCombinationsButton
);
$generateButtons.prop('disabled', false);
$('body').on(
'click',
ProductMap.combinations.generateCombinationsButton,
event => {
// Stop event or it will be caught by click-outside directive and automatically close the modal
event.stopImmediatePropagation();
this.eventEmitter.emit(CombinationEvents.openCombinationsGenerator);
}
);
});
}
/**
* @private
*/
initSubmittableInputs() {
const combinationToken = this.getCombinationToken();
const { quantityKey } = CombinationsMap.combinationItemForm;
const { impactOnPriceKey } = CombinationsMap.combinationItemForm;
const { referenceKey } = CombinationsMap.combinationItemForm;
const { tokenKey } = CombinationsMap.combinationItemForm;
/* eslint-disable */
new SubmittableInput(CombinationsMap.quantityInputWrapper, input =>
this.combinationsService.updateListedCombination(
this.findCombinationId(input),
{
[quantityKey]: input.value,
[tokenKey]: combinationToken
}
)
);
new SubmittableInput(CombinationsMap.impactOnPriceInputWrapper, input =>
this.combinationsService.updateListedCombination(
this.findCombinationId(input),
{
[impactOnPriceKey]: input.value,
[tokenKey]: combinationToken
}
)
);
new SubmittableInput(CombinationsMap.referenceInputWrapper, input =>
this.combinationsService.updateListedCombination(
this.findCombinationId(input),
{
[referenceKey]: input.value,
[tokenKey]: combinationToken
}
)
);
/* eslint-enable */
}
/**
* @private
*/
initSortingColumns() {
this.$combinationsContainer.on(
'click',
CombinationsMap.sortableColumns,
(event) => {
const $sortableColumn = $(event.currentTarget);
const columnName = $sortableColumn.data('sortColName');
if (!columnName) {
return;
}
let direction = $sortableColumn.data('sortDirection');
if (!direction || direction === 'desc') {
direction = 'asc';
} else {
direction = 'desc';
}
// Reset all columns, we need to force the attributes for CSS matching
$(
CombinationsMap.sortableColumns,
this.$combinationsContainer,
).removeData('sortIsCurrent');
$(
CombinationsMap.sortableColumns,
this.$combinationsContainer,
).removeData('sortDirection');
$(
CombinationsMap.sortableColumns,
this.$combinationsContainer,
).removeAttr('data-sort-is-current');
$(
CombinationsMap.sortableColumns,
this.$combinationsContainer,
).removeAttr('data-sort-direction');
// Set correct data in current column, we need to force the attributes for CSS matching
$sortableColumn.data('sortIsCurrent', 'true');
$sortableColumn.data('sortDirection', direction);
$sortableColumn.attr('data-sort-is-current', 'true');
$sortableColumn.attr('data-sort-direction', direction);
// Finally update list
this.combinationsService.setOrderBy(columnName, direction);
this.paginator.paginate(1);
},
);
}
/**
* @param {HTMLElement} button
*
* @private
*/
async removeCombination(button) {
try {
const $deleteButton = $(button);
const modal = new ConfirmModal(
{
id: 'modal-confirm-delete-combination',
confirmTitle: $deleteButton.data('modal-title'),
confirmMessage: $deleteButton.data('modal-message'),
confirmButtonLabel: $deleteButton.data('modal-apply'),
closeButtonLabel: $deleteButton.data('modal-cancel'),
confirmButtonClass: 'btn-danger',
closable: true,
},
async () => {
const response = await this.combinationsService.removeCombination(
this.findCombinationId(button),
);
$.growl({message: response.message});
this.eventEmitter.emit(CombinationEvents.refreshCombinationList);
},
);
modal.show();
} catch (error) {
const errorMessage = error.responseJSON
? error.responseJSON.error
: error;
$.growl.error({message: errorMessage});
}
}
/**
* @param {HTMLElement} checkedInput
*
* @private
*/
async updateDefaultCombination(checkedInput) {
const checkedInputs = this.$combinationsContainer.find(
`${CombinationsMap.isDefaultInputsSelector}:checked`,
);
const checkedDefaultId = this.findCombinationId(checkedInput);
await this.combinationsService.updateListedCombination(checkedDefaultId, {
'combination_item[is_default]': checkedInput.value,
'combination_item[_token]': this.getCombinationToken(),
});
$.each(checkedInputs, (index, input) => {
if (this.findCombinationId(input) !== checkedDefaultId) {
$(input).prop('checked', false);
}
});
}
/**
* @returns {String}
*/
getCombinationToken() {
return $(CombinationsMap.combinationsContainer).data('combinationToken');
}
/**
* @param {HTMLElement} input of the same table row
*
* @returns {Number}
*
* @private
*/
findCombinationId(input) {
return $(input)
.closest('tr')
.find(this.combinationIdInputsSelector)
.val();
}
}