first commit
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
|
||||
export default function () {
|
||||
$(document).ready(() => {
|
||||
$('.js-attribute-checkbox').change((event) => {
|
||||
if ($(event.target).is(':checked')) {
|
||||
if ($(`.token[data-value="${$(event.target).data('value')}"] .close`).length === 0) {
|
||||
$('#form_step3_attributes').tokenfield(
|
||||
'createToken',
|
||||
{value: $(event.target).data('value'), label: $(event.target).data('label')},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$(`.token[data-value="${$(event.target).data('value')}"] .close`).click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#form_step3_attributes')
|
||||
.on('tokenfield:createdtoken', (e) => {
|
||||
if (!$(`.js-attribute-checkbox[data-value="${e.attrs.value}"]`).is(':checked')) {
|
||||
$(`.js-attribute-checkbox[data-value="${e.attrs.value}"]`).prop('checked', true);
|
||||
}
|
||||
})
|
||||
.on('tokenfield:removedtoken', (e) => {
|
||||
if ($(`.js-attribute-checkbox[data-value="${e.attrs.value}"]`).is(':checked')) {
|
||||
$(`.js-attribute-checkbox[data-value="${e.attrs.value}"]`).prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
$('input.form-control[counter], textarea.form-control:not(.autoload_rte)[counter]').each(function () {
|
||||
const counter = $(this).attr('counter');
|
||||
|
||||
if (typeof counter === 'undefined' || counter === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleCounter($(this));
|
||||
$(this).on('input', function () {
|
||||
handleCounter($(this));
|
||||
});
|
||||
|
||||
function handleCounter(object) {
|
||||
const counterObject = $(object).attr('counter');
|
||||
const counterType = $(object).attr('counterType');
|
||||
const max = $(object).val().length;
|
||||
|
||||
$(object).parent().find('span.currentLength').text(max);
|
||||
if (counterType !== 'recommended' && max > counterObject) {
|
||||
$(object).parent().find('span.maxLength').addClass('text-danger');
|
||||
} else {
|
||||
$(object).parent().find('span.maxLength').removeClass('text-danger');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
|
||||
export default function () {
|
||||
/**
|
||||
* scroll right to show radio buttons on the category tree
|
||||
*/
|
||||
const scrollCategoryTree = function scrollCategoryTree() {
|
||||
const $categoryTreeOverflow = $('.category-tree-overflow');
|
||||
const leftPos = $categoryTreeOverflow.width();
|
||||
$categoryTreeOverflow.animate({scrollLeft: leftPos}, 200);
|
||||
};
|
||||
|
||||
const treeAction = (treeState) => {
|
||||
if (treeState === 'expand') {
|
||||
$('.js-categories-tree ul').show();
|
||||
$('.more').toggleClass('less');
|
||||
// scroll right to see the radio buttons
|
||||
scrollCategoryTree();
|
||||
} else {
|
||||
$('.js-categories-tree ul:not(.category-tree)').hide();
|
||||
$('.less').toggleClass('more');
|
||||
}
|
||||
};
|
||||
|
||||
$('#categories-tree-expand').on('click', () => {
|
||||
treeAction('expand');
|
||||
$('#categories-tree-expand').hide();
|
||||
$('#categories-tree-reduce').show();
|
||||
});
|
||||
$('#categories-tree-reduce').on('click', () => {
|
||||
treeAction('collapse');
|
||||
$('#categories-tree-reduce').hide();
|
||||
$('#categories-tree-expand').show();
|
||||
});
|
||||
|
||||
// scroll right to see the radio buttons
|
||||
$('.category-tree-overflow .checkbox').on('click', (e) => {
|
||||
if (!$(e.target).is('input')) {
|
||||
// do not scroll if (un)checking some inputs
|
||||
scrollCategoryTree();
|
||||
}
|
||||
});
|
||||
|
||||
$('.category-tree-overflow .checkbox label').on('click', (e) => {
|
||||
if (!$(e.target).is('input')) {
|
||||
// do not scroll if (un)checking some inputs
|
||||
scrollCategoryTree();
|
||||
}
|
||||
});
|
||||
}
|
||||
213
admin-kalsport/themes/new-theme/js/product-page/combination.js
Normal file
213
admin-kalsport/themes/new-theme/js/product-page/combination.js
Normal file
@@ -0,0 +1,213 @@
|
||||
const {$} = window;
|
||||
|
||||
export default function () {
|
||||
$(document).ready(() => {
|
||||
const $jsCombinationsList = $('.js-combinations-list');
|
||||
|
||||
// If we are not on the product page, return
|
||||
if ($jsCombinationsList.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idsProductAttribute = $jsCombinationsList.data('ids-product-attribute').toString().split(',');
|
||||
const refreshImagesUrl = $jsCombinationsList
|
||||
.attr('data-action-refresh-images')
|
||||
.replace(/form-images\/\d+/, `form-images/${$jsCombinationsList.data('id-product')}`);
|
||||
const idsCount = idsProductAttribute.length;
|
||||
const step = 50;
|
||||
let currentCount = 0;
|
||||
|
||||
$.get(refreshImagesUrl).then((response) => {
|
||||
if (idsCount !== 0) {
|
||||
getCombinations(response);
|
||||
}
|
||||
});
|
||||
|
||||
$('#create-combinations').click((event) => {
|
||||
event.preventDefault();
|
||||
window.form.send(false, false, generate);
|
||||
});
|
||||
|
||||
const productDropzone = window.Dropzone.forElement('#product-images-dropzone');
|
||||
const updateCombinationImages = function () {
|
||||
const productAttributeIds = $.map(
|
||||
$('.js-combinations-list .combination'),
|
||||
(combination) => $(combination).data('index'),
|
||||
);
|
||||
|
||||
$.get(refreshImagesUrl).then((response) => {
|
||||
refreshImagesCombination(response, productAttributeIds);
|
||||
});
|
||||
};
|
||||
productDropzone.on('success', updateCombinationImages);
|
||||
|
||||
$(document).on('click', '#form .product-combination-image', function () {
|
||||
const input = $(this).find('input');
|
||||
const isChecked = input.prop('checked');
|
||||
input.prop('checked', !isChecked);
|
||||
$(this).toggleClass('img-highlight', !isChecked);
|
||||
refreshDefaultImage();
|
||||
});
|
||||
|
||||
$('#product_combination_bulk_impact_on_price_ti, #product_combination_bulk_impact_on_price_te').keyup(function () {
|
||||
const self = $(this);
|
||||
const price = window.priceCalculation.normalizePrice(self.val());
|
||||
|
||||
if (self.attr('id') === 'product_combination_bulk_impact_on_price_ti') {
|
||||
$('#product_combination_bulk_impact_on_price_te').val(window.priceCalculation.removeCurrentTax(price)).change();
|
||||
} else {
|
||||
$('#product_combination_bulk_impact_on_price_ti').val(window.priceCalculation.addCurrentTax(price)).change();
|
||||
}
|
||||
});
|
||||
|
||||
const getCombinations = (combinationsImages) => {
|
||||
const $jsCombinationsBulkForm = $('#combinations-bulk-form');
|
||||
|
||||
if (!$jsCombinationsBulkForm.hasClass('inactive')) {
|
||||
$jsCombinationsBulkForm.addClass('inactive');
|
||||
}
|
||||
|
||||
const $combinationsUrl = getCombinationsUrl();
|
||||
|
||||
if ($combinationsUrl === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.get($combinationsUrl).then((resp) => {
|
||||
$('#loading-attribute').before(resp);
|
||||
refreshImagesCombination(combinationsImages, idsProductAttribute.slice(currentCount, currentCount + step));
|
||||
currentCount += step;
|
||||
if (currentCount < idsCount) {
|
||||
getCombinations(combinationsImages);
|
||||
} else {
|
||||
activateCombinationsBulk();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Retrieve URL to get a set of combination forms from data attribute
|
||||
* Concatenate ids_product_attribute to load from a slice of idsProductAttribute depending of step and last set
|
||||
*/
|
||||
const getCombinationsUrl = () => {
|
||||
const $numbers = idsProductAttribute.slice(currentCount, currentCount + step).join('-');
|
||||
|
||||
if ($numbers.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $jsCombinationsList
|
||||
.data('combinations-url')
|
||||
.replace(
|
||||
':numbers',
|
||||
$numbers,
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
const activateCombinationsBulk = () => {
|
||||
const $jsCombinationsBulkForm = $('#combinations-bulk-form');
|
||||
|
||||
if ($jsCombinationsBulkForm.hasClass('inactive')) {
|
||||
$jsCombinationsBulkForm.removeClass('inactive');
|
||||
$('#loading-attribute').fadeOut(1000).remove();
|
||||
$('[data-toggle="popover"]').popover();
|
||||
}
|
||||
};
|
||||
|
||||
const refreshImagesCombination = (combinationsImages, idsProductAttribute) => {
|
||||
$.each(idsProductAttribute, (index, value) => {
|
||||
const $combinationElem = $(`.combination[data="${value}"]`);
|
||||
const $index = $combinationElem.attr('data-index');
|
||||
let $imagesElem = $combinationElem.find('.images');
|
||||
let html = '';
|
||||
|
||||
if ($imagesElem.length === 0) {
|
||||
$imagesElem = $(`#combination_${$index}_id_image_attr`);
|
||||
}
|
||||
|
||||
$.each(combinationsImages[value], (key, image) => {
|
||||
/* eslint-disable max-len */
|
||||
html += `<div class="product-combination-image ${(image.id_image_attr ? 'img-highlight' : '')}">
|
||||
<input type="checkbox" name="combination_${$index}[id_image_attr][]" value="${image.id}" ${(image.id_image_attr ? 'checked="checked"' : '')}>
|
||||
<img src="${image.base_image_url}-small_default.${image.format}" alt="" />
|
||||
</div>`;
|
||||
/* eslint-enabled max-len */
|
||||
});
|
||||
$imagesElem.html(html);
|
||||
$combinationElem.fadeIn(1000);
|
||||
});
|
||||
|
||||
refreshDefaultImage();
|
||||
};
|
||||
|
||||
const refreshDefaultImage = () => {
|
||||
const productCoverImageElem = $('#product-images-dropzone').find('.iscover');
|
||||
let productDefaultImageUrl = null;
|
||||
|
||||
/** get product cover image */
|
||||
if (productCoverImageElem.length === 1) {
|
||||
const imgElem = productCoverImageElem.parent().find('.dz-image');
|
||||
|
||||
/** Dropzone.js workaround : If this is a fresh upload image, look up for an img, else find a background url */
|
||||
if (imgElem.find('img').length) {
|
||||
productDefaultImageUrl = imgElem.find('img').attr('src');
|
||||
} else {
|
||||
productDefaultImageUrl = imgElem.css('background-image')
|
||||
.replace(/^url\(["']?/, '')
|
||||
.replace(/["']?\)$/, '');
|
||||
}
|
||||
}
|
||||
|
||||
$.each($('#form .combination-form'), (key, elem) => {
|
||||
let defaultImageUrl = productDefaultImageUrl;
|
||||
|
||||
/** get first selected image */
|
||||
const defaultImageElem = $(elem).find('.product-combination-image input:checked:first');
|
||||
|
||||
if (defaultImageElem.length === 1) {
|
||||
defaultImageUrl = defaultImageElem.parent().find('img').attr('src');
|
||||
}
|
||||
|
||||
if (defaultImageUrl) {
|
||||
const img = `<img src="${defaultImageUrl}" class="img-responsive" />`;
|
||||
$(`#attribute_${$(elem).attr('data')}`).find('td.img').html(img);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const generate = () => {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: $('#form_step3_attributes').attr('data-action'),
|
||||
data: $('#attributes-generator input.attribute-generator, #form_id_product').serialize(),
|
||||
beforeSend() {
|
||||
$('#create-combinations, #submit, .btn-submit').attr('disabled', 'disabled');
|
||||
},
|
||||
success(response) {
|
||||
window.refreshTotalCombinations(1, $(response.form).filter('.combination.loaded').length);
|
||||
$('#accordion_combinations').append(response.form);
|
||||
window.displayFieldsManager.refresh();
|
||||
const url = $('.js-combinations-list').attr('data-action-refresh-images').replace(/form-images\/\d+/, `form-images/${$('.js-combinations-list').data('id-product')}`);
|
||||
$.get(url)
|
||||
.then((combinationsImages) => {
|
||||
refreshImagesCombination(combinationsImages, response.ids_product_attribute);
|
||||
});
|
||||
|
||||
/** initialize form */
|
||||
$('input.attribute-generator').remove();
|
||||
$('#attributes-generator div.token').remove();
|
||||
$('.js-attribute-checkbox:checked').each(function () {
|
||||
$(this).prop('checked', false);
|
||||
});
|
||||
$('#combinations_thead').fadeIn();
|
||||
},
|
||||
complete() {
|
||||
$('#create-combinations, #submit, .btn-submit').removeAttr('disabled');
|
||||
activateCombinationsBulk();
|
||||
window.supplierCombinations.refresh();
|
||||
window.warehouseCombinations.refresh();
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
69
admin-kalsport/themes/new-theme/js/product-page/index.js
Normal file
69
admin-kalsport/themes/new-theme/js/product-page/index.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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 productHeader from './product-header';
|
||||
import productSearchAutocomplete from './product-search-autocomplete';
|
||||
import categoryTree from './category-tree';
|
||||
import attributes from './attributes';
|
||||
import bulkCombination from './product-bulk-combinations';
|
||||
import nestedCategory from './nested-categories';
|
||||
import combination from './combination';
|
||||
import Serp from '../app/utils/serp/index';
|
||||
|
||||
const {$} = window;
|
||||
|
||||
$(() => {
|
||||
productHeader();
|
||||
productSearchAutocomplete();
|
||||
categoryTree();
|
||||
attributes();
|
||||
combination();
|
||||
bulkCombination().init();
|
||||
nestedCategory().init();
|
||||
|
||||
new Serp(
|
||||
{
|
||||
container: '#serp-app',
|
||||
defaultTitle: '.serp-default-title:input',
|
||||
watchedTitle: '.serp-watched-title:input',
|
||||
defaultDescription: '.serp-default-description',
|
||||
watchedDescription: '.serp-watched-description',
|
||||
watchedMetaUrl: '.serp-watched-url:input',
|
||||
},
|
||||
$('#product_form_preview_btn').data('seo-url'),
|
||||
);
|
||||
|
||||
// This is the only script for the module page so there is no specific file for it.
|
||||
$('.modules-list-select').on('change', (e) => {
|
||||
$('.module-render-container').hide();
|
||||
$(`.${e.target.value}`).show();
|
||||
});
|
||||
|
||||
$('.modules-list-button').on('click', (e) => {
|
||||
const target = $(e.target).data('target');
|
||||
$('.module-selection').show();
|
||||
$('.modules-list-select').val(target).trigger('change');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Nested categories management
|
||||
*/
|
||||
export default function () {
|
||||
const nestedCategoriesForm = $('#form_step1_categories');
|
||||
|
||||
return {
|
||||
init() {
|
||||
if (nestedCategoriesForm.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
nestedCategoriesForm.categorytree();
|
||||
this.removeDefaultIfNeeded();
|
||||
|
||||
// now we can select default category from nested Categories even if it's not related from a "code" point of view.
|
||||
nestedCategoriesForm.on('change', 'input.default-category', function updateDefaultCategory() {
|
||||
const categoryId = $(this).val();
|
||||
/* we can't select a default category if category is not selected
|
||||
* that's why we check category first instead of warn user.
|
||||
*/
|
||||
const category = nestedCategoriesForm.find(`input[value="${categoryId}"].category`);
|
||||
const defaultcat = nestedCategoriesForm.find(`input[value="${categoryId}"].default-category`);
|
||||
|
||||
if (category.is(':checked') === false && defaultcat.is(':checked') === true) {
|
||||
category.trigger('click');
|
||||
}
|
||||
window.defaultCategory.check(categoryId);
|
||||
});
|
||||
},
|
||||
removeDefaultIfNeeded() {
|
||||
/**
|
||||
* What if we unselect category when it's a default category ?
|
||||
*/
|
||||
nestedCategoriesForm.on('change', 'input.category', function removeDefaultCategoryIfNotSelected() {
|
||||
const findClosestCheckedCategory = function (category) {
|
||||
const parent = category.closest('ul').parent();
|
||||
const parentCategory = parent.find('> div > label > input');
|
||||
|
||||
if (!parent.is('li')) {
|
||||
return nestedCategoriesForm.find('input[type="checkbox"]:checked').first();
|
||||
} if (parentCategory.prop('checked')) {
|
||||
return parentCategory;
|
||||
}
|
||||
return findClosestCheckedCategory(parent);
|
||||
};
|
||||
|
||||
const categoryId = $(this).val();
|
||||
const category = nestedCategoriesForm.find(`input[value="${categoryId}"].category`);
|
||||
const defaultcat = nestedCategoriesForm.find(`input[value="${categoryId}"].default-category`);
|
||||
|
||||
if (nestedCategoriesForm.find('input[type="checkbox"]').filter(':checked').length < 1) {
|
||||
category.prop('checked', true);
|
||||
} else if (category.is(':checked') === false && defaultcat.is(':checked') === true) {
|
||||
const newCategory = findClosestCheckedCategory(category);
|
||||
|
||||
window.defaultCategory.check(newCategory.val());
|
||||
window.productCategoriesTags.checkDefaultCategory(newCategory.val());
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* Combination bulk actions management
|
||||
*/
|
||||
export default function () {
|
||||
const bulkForm = $('#bulk-combinations-container');
|
||||
const deleteCombinationsBtn = $('#delete-combinations');
|
||||
const applyChangesBtn = $('#apply-on-combinations');
|
||||
const syncedCollection = $('[data-uniqid]');
|
||||
const finalPrice = $('#form_step2_price');
|
||||
const finalPriceIT = $('#form_step2_price_ttc');
|
||||
const finalPriceBasics = $('#form_step1_price_shortcut');
|
||||
const finalPriceBasicsIT = $('#form_step1_price_ttc_shortcut');
|
||||
const ecotaxTI = $('#form_step2_ecotax');
|
||||
|
||||
return {
|
||||
init: function init() {
|
||||
const that = this;
|
||||
// stop propagation on buttons
|
||||
deleteCombinationsBtn.on('click', (event) => {
|
||||
event.preventDefault();
|
||||
that.deleteCombinations();
|
||||
});
|
||||
|
||||
applyChangesBtn.on('click', (event) => {
|
||||
event.preventDefault();
|
||||
that.applyChangesOnCombinations()
|
||||
.hideForm()
|
||||
.resetForm()
|
||||
.unselectCombinations()
|
||||
.submitUpdate();
|
||||
});
|
||||
|
||||
/* if final price change with user interaction, combinations should be impacted */
|
||||
finalPrice.on('blur', () => {
|
||||
this.syncToPricingTab();
|
||||
});
|
||||
|
||||
finalPriceIT.on('blur', () => {
|
||||
this.syncToPricingTab();
|
||||
});
|
||||
|
||||
/* if we use final price shortcuts, also combinations should be impacted */
|
||||
finalPriceBasics.on('blur', () => {
|
||||
this.syncToPricingTab();
|
||||
});
|
||||
|
||||
finalPriceBasicsIT.on('blur', () => {
|
||||
this.syncToPricingTab();
|
||||
});
|
||||
|
||||
ecotaxTI.on('blur', () => {
|
||||
this.syncToPricingTab();
|
||||
});
|
||||
|
||||
syncedCollection.on('DOMSubtreeModified', (event) => {
|
||||
const uniqid = event.target.getAttribute('data-uniqid');
|
||||
const newValue = event.target.innerText;
|
||||
|
||||
const spans = $(`[data-uniqid="${uniqid}"]`);
|
||||
|
||||
spans.each(function () {
|
||||
if ($(this).text() !== newValue) {
|
||||
$(this).text(newValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// bulk select animation
|
||||
$('#toggle-all-combinations').on('change', (event) => {
|
||||
$('#accordion_combinations td:first-child input[type="checkbox"]').each(function () {
|
||||
$(this).prop('checked', $(event.currentTarget).prop('checked'));
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('change', '.js-combination', () => {
|
||||
if ($('.bulk-action').attr('aria-expanded') === 'false' || !$('.js-combination').is(':checked')) {
|
||||
$('.js-collapse').collapse('toggle');
|
||||
}
|
||||
$('span.js-bulk-combinations').text($('input.js-combination:checked').length);
|
||||
});
|
||||
},
|
||||
getSelectedCombinations: function getSelectedCombinations() {
|
||||
const combinations = [];
|
||||
const selectedCombinations = Array.from(
|
||||
$('#accordion_combinations td:first-child input[type="checkbox"]:checked'),
|
||||
);
|
||||
selectedCombinations.forEach((combination) => {
|
||||
const combinationId = combination.getAttribute('data-id');
|
||||
const combinationIndex = combination.getAttribute('data-index');
|
||||
combinations.push(new Combination(combinationId, combinationIndex));
|
||||
});
|
||||
|
||||
return combinations;
|
||||
},
|
||||
applyChangesOnCombinations: function applyChangesOnCombinations() {
|
||||
const values = this.getFormValues();
|
||||
const combinations = this.getSelectedCombinations();
|
||||
combinations.forEach((combination) => {
|
||||
combination.updateForm(values);
|
||||
combination.syncValues(values);
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
deleteCombinations: function deleteCombinations() {
|
||||
const combinations = this.getSelectedCombinations();
|
||||
const combinationsIds = [];
|
||||
combinations.forEach((combination) => {
|
||||
combinationsIds.push(combination.domId);
|
||||
});
|
||||
|
||||
window.modalConfirmation.create(
|
||||
window.translate_javascripts['Are you sure you want to delete the selected item(s)?'],
|
||||
null,
|
||||
{
|
||||
onContinue() {
|
||||
const deletionURL = $(deleteCombinationsBtn).attr('data');
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
data: {
|
||||
'attribute-ids': combinationsIds,
|
||||
},
|
||||
url: deletionURL,
|
||||
beforeSend() {
|
||||
$('#create-combinations, #apply-on-combinations, #submit, .btn-submit').attr('disabled', 'disabled');
|
||||
},
|
||||
success(response) {
|
||||
window.showSuccessMessage(response.message);
|
||||
window.refreshTotalCombinations(-1, combinationsIds.length);
|
||||
$('span.js-bulk-combinations').text('0');
|
||||
combinationsIds.forEach((combinationId) => {
|
||||
const combination = new Combination(combinationId);
|
||||
combination.removeFromDOM();
|
||||
});
|
||||
window.displayFieldsManager.refresh();
|
||||
},
|
||||
error(response) {
|
||||
window.showErrorMessage(jQuery.parseJSON(response.responseText).message);
|
||||
},
|
||||
complete() {
|
||||
$('#create-combinations, #apply-on-combinations, #submit, .btn-submit').removeAttr('disabled');
|
||||
},
|
||||
});
|
||||
},
|
||||
}).show();
|
||||
},
|
||||
getFormValues: function getFormValues() {
|
||||
const values = [];
|
||||
$(bulkForm).find('input').each(function () {
|
||||
if ($(this).val() !== '' && $(this).attr('id') !== 'product_combination_bulk__token') {
|
||||
values.push({
|
||||
id: $(this).attr('id'),
|
||||
value: $(this).val(),
|
||||
});
|
||||
}
|
||||
});
|
||||
return values;
|
||||
},
|
||||
resetForm: function resetForm() {
|
||||
bulkForm.find('input').val('');
|
||||
|
||||
return this;
|
||||
},
|
||||
unselectCombinations: function unselectCombinations() {
|
||||
// Use of the bulk action button. It has an event listener to unselect all the combinations
|
||||
$('#toggle-all-combinations').prop('checked', false);
|
||||
|
||||
return this;
|
||||
},
|
||||
hideForm: function toggleForm() {
|
||||
bulkForm.collapse('hide');
|
||||
|
||||
return this;
|
||||
},
|
||||
submitUpdate: function submitUpdate() {
|
||||
const globalProductSubmitButton = $('#form'); // @todo: choose a better identifier
|
||||
globalProductSubmitButton.submit();
|
||||
},
|
||||
syncToPricingTab: function syncToPricingTab() {
|
||||
$('tr.combination').toArray().forEach((item) => {
|
||||
const tableRow = $(`#${item.id}`);
|
||||
// We need this because there is a specific data="smthg" attribute so we can't use data() function
|
||||
const attributeId = tableRow.attr('data');
|
||||
|
||||
// Get combination final price value from combination form
|
||||
const combinationFinalPrice = window.priceCalculation.getCombinationFinalPriceTaxExcludedById(attributeId);
|
||||
const combinationFinalPriceLabel = tableRow.find('.attribute-finalprice span.final-price');
|
||||
combinationFinalPriceLabel.html(combinationFinalPrice);
|
||||
|
||||
// Update ecotax preview (tax included)
|
||||
let combinationEcotaxTI = window.priceCalculation.getCombinationEcotaxTaxIncludedById(attributeId);
|
||||
|
||||
if (combinationEcotaxTI === 0) {
|
||||
combinationEcotaxTI = window.priceCalculation.getProductEcotaxTaxIncluded();
|
||||
}
|
||||
const ecoTaxLabel = tableRow.find('.attribute-finalprice span.attribute-ecotax');
|
||||
ecoTaxLabel.html(Number(window.ps_round(combinationEcotaxTI, 2)).toFixed(2)); // 2 digits for short
|
||||
const ecoTaxPreview = tableRow.find('.attribute-finalprice .attribute-ecotax-preview');
|
||||
ecoTaxPreview.toggleClass('d-none', Number(combinationEcotaxTI) === 0);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class Combination {
|
||||
constructor(domId, index) {
|
||||
this.inputBulkPattern = 'product_combination_bulk_';
|
||||
this.inputPattern = `combination_${index}_`;
|
||||
this.domId = domId;
|
||||
this.appId = `attribute_${this.domId}`;
|
||||
this.element = $(`#${this.appId}`);
|
||||
this.form = $(`#combination_form_${this.domId}`);
|
||||
}
|
||||
|
||||
isSelected() {
|
||||
return this.element.checked;
|
||||
}
|
||||
|
||||
removeFromDOM() {
|
||||
$(this.element).remove();
|
||||
}
|
||||
|
||||
updateForm(values) {
|
||||
values.forEach((valueObject) => {
|
||||
const valueId = valueObject.id.substr(this.inputBulkPattern.length);
|
||||
const $field = $(`#${this.convertInput(valueId)}`);
|
||||
|
||||
if ($field.is(':checkbox')) {
|
||||
$field.prop('checked', !!valueObject.value);
|
||||
} else {
|
||||
$field.val(valueObject.value);
|
||||
}
|
||||
});
|
||||
return this.form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the related input field in legacy form from
|
||||
* bulk form field
|
||||
*
|
||||
* @param bulkInput
|
||||
* @returns {string}
|
||||
*/
|
||||
convertInput(bulkInput) {
|
||||
let convertedInput = '';
|
||||
|
||||
switch (bulkInput) {
|
||||
case 'quantity':
|
||||
case 'reference':
|
||||
case 'minimal_quantity':
|
||||
case 'low_stock_threshold':
|
||||
case 'low_stock_alert':
|
||||
convertedInput = `${this.inputPattern}attribute_${bulkInput}`;
|
||||
break;
|
||||
case 'cost_price':
|
||||
convertedInput = `${this.inputPattern}attribute_wholesale_price`;
|
||||
break;
|
||||
case 'date_availability':
|
||||
convertedInput = `${this.inputPattern}available_date_attribute`;
|
||||
break;
|
||||
case 'impact_on_weight':
|
||||
convertedInput = `${this.inputPattern}attribute_weight`;
|
||||
break;
|
||||
case 'impact_on_price_te':
|
||||
convertedInput = `${this.inputPattern}attribute_price`;
|
||||
break;
|
||||
case 'impact_on_price_ti':
|
||||
convertedInput = `${this.inputPattern}attribute_priceTI`;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return convertedInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync values with fast bulk edit form of each combination
|
||||
*
|
||||
* @param values
|
||||
* @returns {bool}
|
||||
*/
|
||||
syncValues(values) {
|
||||
values.forEach((valueObject) => {
|
||||
let valueId = valueObject.id.substr(this.inputBulkPattern.length);
|
||||
const {value} = valueObject;
|
||||
|
||||
const syncedProperties = [
|
||||
'quantity',
|
||||
'impact_on_price_te',
|
||||
];
|
||||
|
||||
if (syncedProperties.indexOf(valueId) !== -1) {
|
||||
valueId = valueId === 'quantity' ? 'quantity' : 'price';
|
||||
const input = $(`#attribute_${this.domId} .attribute-${valueId} input`);
|
||||
input.val(value);
|
||||
input.change();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
|
||||
const {$} = window;
|
||||
|
||||
export default function () {
|
||||
const $defaultArrowWidth = 35;
|
||||
const $arrow = $('.js-arrow');
|
||||
const $tabs = $('.js-tabs');
|
||||
const $navTabs = $('.js-nav-tabs');
|
||||
|
||||
let $positions;
|
||||
let $moveTo = 0;
|
||||
let $tabWidth = 0;
|
||||
let $navWidth = $defaultArrowWidth;
|
||||
let $widthWithTabs = 0;
|
||||
|
||||
$navTabs.find('li').each((index, item) => {
|
||||
$navWidth += $(item).width();
|
||||
});
|
||||
|
||||
$widthWithTabs = $navWidth + ($defaultArrowWidth * 2);
|
||||
|
||||
$navTabs.width($widthWithTabs);
|
||||
|
||||
$navTabs.find('[data-toggle="tab"]').on('click', (e) => {
|
||||
if (!$(e.target).hasClass('active')) {
|
||||
$('#form_content > .form-contenttab').removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
$arrow.on('click', (e) => {
|
||||
if ($arrow.is(':visible')) {
|
||||
$tabWidth = $tabs.width();
|
||||
$positions = $navTabs.position();
|
||||
|
||||
$moveTo = '-=0';
|
||||
if ($(e.currentTarget).hasClass('right-arrow')) {
|
||||
if (($tabWidth - $positions.left) < $navWidth) {
|
||||
$moveTo = `-=${$tabWidth}`;
|
||||
}
|
||||
} else if ($positions.left < $defaultArrowWidth) {
|
||||
$moveTo = `+=${$tabWidth}`;
|
||||
}
|
||||
|
||||
$navTabs.animate(
|
||||
{
|
||||
left: $moveTo,
|
||||
},
|
||||
400,
|
||||
'easeOutQuad',
|
||||
() => {
|
||||
if ($(e.currentTarget).hasClass('right-arrow')) {
|
||||
$('.left-arrow').addClass('visible');
|
||||
$('.right-arrow').removeClass('visible');
|
||||
} else {
|
||||
$('.right-arrow').addClass('visible');
|
||||
$('.left-arrow').removeClass('visible');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 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 Bloodhound from 'typeahead.js';
|
||||
|
||||
export default function () {
|
||||
$(document).ready(() => {
|
||||
$('.autocomplete-search').each(function () {
|
||||
loadAutocomplete($(this), false);
|
||||
});
|
||||
|
||||
$('.autocomplete-search').on('buildTypeahead', function () {
|
||||
loadAutocomplete($(this), true);
|
||||
});
|
||||
});
|
||||
|
||||
function loadAutocomplete(object, reset) {
|
||||
const autocompleteObject = $(object);
|
||||
const autocompleteFormId = autocompleteObject.attr('data-formid');
|
||||
const formId = `#${autocompleteFormId}-data .delete`;
|
||||
const autocompleteSource = `${autocompleteFormId}_source`;
|
||||
|
||||
if (reset === true) {
|
||||
$(`#${autocompleteFormId}`).typeahead('destroy');
|
||||
}
|
||||
|
||||
$(document).on('click', formId, (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
window.modalConfirmation.create(
|
||||
window.translate_javascripts['Are you sure you want to delete this item?'],
|
||||
null,
|
||||
{
|
||||
onContinue: () => {
|
||||
$(e.target).parents('.media').remove();
|
||||
|
||||
// Save current product after its related product has been removed
|
||||
$('#submit').click();
|
||||
},
|
||||
}).show();
|
||||
});
|
||||
|
||||
document[autocompleteSource] = new Bloodhound({
|
||||
datumTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
identify(obj) {
|
||||
return obj[autocompleteObject.attr('data-mappingvalue')];
|
||||
},
|
||||
remote: {
|
||||
url: autocompleteObject.attr('data-remoteurl'),
|
||||
cache: false,
|
||||
wildcard: '%QUERY',
|
||||
transform(response) {
|
||||
if (!response) {
|
||||
return [];
|
||||
}
|
||||
return response;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// define typeahead
|
||||
$(`#${autocompleteFormId}`).typeahead({
|
||||
limit: 20,
|
||||
minLength: 2,
|
||||
highlight: true,
|
||||
cache: false,
|
||||
hint: false,
|
||||
}, {
|
||||
display: autocompleteObject.attr('data-mappingname'),
|
||||
source: document[`${autocompleteFormId}_source`],
|
||||
limit: 30,
|
||||
templates: {
|
||||
suggestion(item) {
|
||||
return `<div><img src="${item.image}" style="width:50px" /> ${item.name}</div>`;
|
||||
},
|
||||
},
|
||||
}).bind('typeahead:select', (e, suggestion) => {
|
||||
// if collection length is up to limit, return
|
||||
|
||||
const formIdItem = $(`#${autocompleteFormId}-data li`);
|
||||
const autocompleteFormLimit = parseInt(autocompleteObject.attr('data-limit'), 10);
|
||||
|
||||
if (autocompleteFormLimit !== 0 && formIdItem.length >= autocompleteFormLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let value = suggestion[autocompleteObject.attr('data-mappingvalue')];
|
||||
|
||||
if (suggestion.id_product_attribute) {
|
||||
value = `${value},${suggestion.id_product_attribute}`;
|
||||
}
|
||||
|
||||
const tplcollection = $(`#tplcollection-${autocompleteFormId}`);
|
||||
const tplcollectionHtml = tplcollection
|
||||
.html()
|
||||
.replace('%s', suggestion[autocompleteObject.attr('data-mappingname')]);
|
||||
|
||||
const html = `<li class="media">
|
||||
<div class="media-left">
|
||||
<img class="media-object image" src="${suggestion.image}" />
|
||||
</div>
|
||||
<div class="media-body media-middle">
|
||||
${tplcollectionHtml}
|
||||
</div>
|
||||
<input type="hidden" name="${autocompleteObject.attr('data-fullname')}[data][]" value="${value}" />
|
||||
</li>`;
|
||||
|
||||
$(`#${autocompleteFormId}-data`).append(html);
|
||||
|
||||
return true;
|
||||
}).bind('typeahead:close', (e) => {
|
||||
$(e.target).val('');
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user