first commit
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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 class EntityFieldsValidator {
|
||||
/**
|
||||
* Validates entity fields
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
static validate() {
|
||||
$('.js-validation-error').addClass('d-none');
|
||||
|
||||
return this.checkDuplicateSelectedValues() && this.checkRequiredFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are no duplicate selected values.
|
||||
*
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
static checkDuplicateSelectedValues() {
|
||||
const uniqueFields = [];
|
||||
let valid = true;
|
||||
|
||||
$('.js-entity-field select').each(function () {
|
||||
const value = $(this).val();
|
||||
|
||||
if (value === 'no') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($.inArray(value, uniqueFields) !== -1) {
|
||||
valid = false;
|
||||
$('.js-duplicate-columns-warning').removeClass('d-none');
|
||||
return;
|
||||
}
|
||||
|
||||
uniqueFields.push(value);
|
||||
});
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all required fields are selected.
|
||||
*
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
static checkRequiredFields() {
|
||||
const requiredImportFields = $('.js-import-data-table').data('required-fields');
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
for (const key in requiredImportFields) {
|
||||
if ($(`option[value="${requiredImportFields[key]}"]:selected`).length === 0) {
|
||||
$('.js-missing-column-warning').removeClass('d-none');
|
||||
$('.js-missing-column').text($(`option[value="${requiredImportFields[key]}"]:first`).text());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ImportBatchSizeCalculator calculates the import batch size.
|
||||
* Import batch size is the maximum number of records that
|
||||
* the import should handle in one batch.
|
||||
*/
|
||||
export default class ImportBatchSizeCalculator {
|
||||
constructor() {
|
||||
// Target execution time in milliseconds.
|
||||
this.targetExecutionTime = 5000;
|
||||
|
||||
// Maximum batch size increase multiplier.
|
||||
this.maxAcceleration = 4;
|
||||
|
||||
// Minimum and maximum import batch sizes.
|
||||
this.minBatchSize = 5;
|
||||
this.maxBatchSize = 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the start of the import operation.
|
||||
* Must be executed before starting the import,
|
||||
* to be able to calculate the import batch size later on.
|
||||
*/
|
||||
markImportStart() {
|
||||
this.importStartTime = new Date().getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the end of the import operation.
|
||||
* Must be executed after the import operation finishes,
|
||||
* to be able to calculate the import batch size later on.
|
||||
*/
|
||||
markImportEnd() {
|
||||
this.actualExecutionTime = new Date().getTime() - this.importStartTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates how much the import execution time can be increased to still be acceptable.
|
||||
*
|
||||
* @returns {number}
|
||||
* @private
|
||||
*/
|
||||
calculateAcceleration() {
|
||||
return Math.min(this.maxAcceleration, this.targetExecutionTime / this.actualExecutionTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the recommended import batch size.
|
||||
*
|
||||
* @param {number} currentBatchSize current import batch size
|
||||
* @param {number} maxBatchSize greater than zero, the batch size that shouldn't be exceeded
|
||||
*
|
||||
* @returns {number} recommended import batch size
|
||||
*/
|
||||
calculateBatchSize(currentBatchSize, maxBatchSize = 0) {
|
||||
if (!this.importStartTime) {
|
||||
throw new Error('Import start is not marked.');
|
||||
}
|
||||
|
||||
if (!this.actualExecutionTime) {
|
||||
throw new Error('Import end is not marked.');
|
||||
}
|
||||
|
||||
const candidates = [
|
||||
this.maxBatchSize,
|
||||
Math.max(this.minBatchSize, Math.floor(currentBatchSize * this.calculateAcceleration())),
|
||||
];
|
||||
|
||||
if (maxBatchSize > 0) {
|
||||
candidates.push(maxBatchSize);
|
||||
}
|
||||
|
||||
return Math.min(...candidates);
|
||||
}
|
||||
}
|
||||
@@ -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 ImportMatchConfiguration from './ImportMatchConfiguration';
|
||||
import ImportDataTable from './ImportDataTable';
|
||||
import EntityFieldsValidator from './EntityFieldsValidator';
|
||||
import Importer from './Importer';
|
||||
|
||||
export default class ImportDataPage {
|
||||
constructor() {
|
||||
new ImportMatchConfiguration();
|
||||
new ImportDataTable();
|
||||
this.importer = new Importer();
|
||||
|
||||
$(document).on('click', '.js-process-import', (e) => this.importHandler(e));
|
||||
$(document).on('click', '.js-abort-import', () => this.importer.requestCancelImport());
|
||||
$(document).on('click', '.js-close-modal', () => this.importer.progressModal.hide());
|
||||
$(document).on('click', '.js-continue-import', () => this.importer.continueImport());
|
||||
}
|
||||
|
||||
/**
|
||||
* Import process event handler
|
||||
*/
|
||||
importHandler(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!EntityFieldsValidator.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const configuration = {};
|
||||
|
||||
// Collect the configuration from the form into an array.
|
||||
$('.import-data-configuration-form').find(
|
||||
'#skip, select[name^=type_value], #csv, #iso_lang, #entity,'
|
||||
+ '#truncate, #match_ref, #regenerate, #forceIDs, #sendemail,'
|
||||
+ '#separator, #multiple_value_separator',
|
||||
).each((index, $input) => {
|
||||
configuration[$($input).attr('name')] = $($input).val();
|
||||
});
|
||||
|
||||
this.importer.import(
|
||||
$('.js-import-process-button').data('import_url'),
|
||||
configuration,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
const $importDataTable = $('.js-import-data-table');
|
||||
|
||||
/**
|
||||
* Pagination directions - forward and backward.
|
||||
*/
|
||||
const FORWARD = 'forward';
|
||||
const BACKWARD = 'backward';
|
||||
|
||||
export default class ImportDataTable {
|
||||
constructor() {
|
||||
this.numberOfColumnsPerPage = this.getNumberOfVisibleColumns();
|
||||
this.totalNumberOfColumns = this.getTotalNumberOfColumns();
|
||||
|
||||
$('.js-import-next-page').on('click', () => this.importNextPageHandler());
|
||||
$('.js-import-previous-page').on('click', () => this.importPreviousPageHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the next page action in import data table.
|
||||
*/
|
||||
importNextPageHandler() {
|
||||
this.importPaginationHandler(FORWARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the previous page action in import data table.
|
||||
*/
|
||||
importPreviousPageHandler() {
|
||||
this.importPaginationHandler(BACKWARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the forward and back buttons actions in the import table.
|
||||
*
|
||||
* @param {string} direction
|
||||
* @private
|
||||
*/
|
||||
importPaginationHandler(direction) {
|
||||
const $currentPageElements = $importDataTable.find('th:visible,td:visible');
|
||||
const $oppositePaginationButton = direction === FORWARD ? $('.js-import-next-page') : $('.js-import-previous-page');
|
||||
let lastVisibleColumnFound = false;
|
||||
let numberOfVisibleColumns = 0;
|
||||
let $tableColumns = $importDataTable.find('th');
|
||||
|
||||
if (direction === BACKWARD) {
|
||||
// If going backward - reverse the table columns array and use the same logic as forward
|
||||
$tableColumns = $($tableColumns.toArray().reverse());
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
for (const index in $tableColumns) {
|
||||
if (Number.isNaN(index)) {
|
||||
// Reached the last column - hide the opposite pagination button
|
||||
this.hide($oppositePaginationButton);
|
||||
break;
|
||||
}
|
||||
|
||||
// Searching for last visible column
|
||||
if ($($tableColumns[index]).is(':visible')) {
|
||||
lastVisibleColumnFound = true;
|
||||
/* eslint-disable-next-line no-continue */
|
||||
continue;
|
||||
}
|
||||
|
||||
// If last visible column was found - show the column after it
|
||||
if (lastVisibleColumnFound) {
|
||||
// If going backward, the column index must be counted from the last element
|
||||
const showColumnIndex = direction === BACKWARD ? this.totalNumberOfColumns - 1 - index : index;
|
||||
this.showTableColumnByIndex(showColumnIndex);
|
||||
numberOfVisibleColumns += 1;
|
||||
|
||||
// If number of visible columns per page is already reached - break the loop
|
||||
if (numberOfVisibleColumns >= this.numberOfColumnsPerPage) {
|
||||
this.hide($oppositePaginationButton);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide all the columns from previous page
|
||||
this.hide($currentPageElements);
|
||||
|
||||
// If the first column in the table is not visible - show the "previous" pagination arrow
|
||||
if (!$importDataTable.find('th:first').is(':visible')) {
|
||||
this.show($('.js-import-previous-page'));
|
||||
}
|
||||
|
||||
// If the last column in the table is not visible - show the "next" pagination arrow
|
||||
if (!$importDataTable.find('th:last').is(':visible')) {
|
||||
this.show($('.js-import-next-page'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of currently visible columns in the import data table.
|
||||
*
|
||||
* @returns {number}
|
||||
* @private
|
||||
*/
|
||||
getNumberOfVisibleColumns() {
|
||||
return $importDataTable.find('th:visible').length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of columns in the import data table.
|
||||
*
|
||||
* @returns {number}
|
||||
* @private
|
||||
*/
|
||||
getTotalNumberOfColumns() {
|
||||
return $importDataTable.find('th').length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the elements.
|
||||
*
|
||||
* @param $elements
|
||||
* @private
|
||||
*/
|
||||
hide($elements) {
|
||||
$elements.addClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the elements.
|
||||
*
|
||||
* @param $elements
|
||||
* @private
|
||||
*/
|
||||
show($elements) {
|
||||
$elements.removeClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a column from import data table by given index
|
||||
*
|
||||
* @param columnIndex
|
||||
* @private
|
||||
*/
|
||||
showTableColumnByIndex(columnIndex) {
|
||||
// Increasing the index because nth-child calculates from 1 and index starts from 0
|
||||
const colIndex = columnIndex + 1;
|
||||
|
||||
this.show($importDataTable.find(`th:nth-child(${colIndex})`));
|
||||
this.show($importDataTable.find('tbody > tr').find(`td:nth-child(${colIndex})`));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Class is responsible for import match configuration
|
||||
* in Advanced parameters -> Import -> step 2 form.
|
||||
*/
|
||||
export default class ImportMatchConfiguration {
|
||||
/**
|
||||
* Initializes all the processes related with import matches.
|
||||
*/
|
||||
constructor() {
|
||||
this.loadEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all events for data match configuration.
|
||||
*/
|
||||
loadEvents() {
|
||||
$(document).on('click', '.js-save-import-match', (event) => this.save(event));
|
||||
$(document).on('click', '.js-load-import-match', (event) => this.load(event));
|
||||
$(document).on('click', '.js-delete-import-match', (event) => this.delete(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the import match configuration.
|
||||
*/
|
||||
save(event) {
|
||||
event.preventDefault();
|
||||
const ajaxUrl = $('.js-save-import-match').attr('data-url');
|
||||
const formData = $('.import-data-configuration-form').serialize();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: ajaxUrl,
|
||||
data: formData,
|
||||
}).then((response) => {
|
||||
if (typeof response.errors !== 'undefined' && response.errors.length) {
|
||||
this.showErrorPopUp(response.errors);
|
||||
} else if (response.matches.length > 0) {
|
||||
const $dataMatchesDropdown = this.matchesDropdown;
|
||||
|
||||
Object.values(response.matches).forEach((resp) => {
|
||||
const $existingMatch = $dataMatchesDropdown.find(`option[value=${resp.id_import_match}]`);
|
||||
|
||||
// If match already exists with same id - do nothing
|
||||
if ($existingMatch.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Append the new option to the matches dropdown
|
||||
this.appendOptionToDropdown(
|
||||
$dataMatchesDropdown,
|
||||
resp.name,
|
||||
resp.id_import_match,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the import match.
|
||||
*/
|
||||
load(event) {
|
||||
event.preventDefault();
|
||||
const ajaxUrl = $('.js-load-import-match').attr('data-url');
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: ajaxUrl,
|
||||
data: {
|
||||
import_match_id: this.matchesDropdown.val(),
|
||||
},
|
||||
}).then((response) => {
|
||||
if (response) {
|
||||
this.rowsSkipInput.val(response.skip);
|
||||
|
||||
const entityFields = response.match.split('|');
|
||||
Object.keys(entityFields).forEach((i) => {
|
||||
$(`#type_value_${i}`).val(entityFields[i]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the import match.
|
||||
*/
|
||||
delete(event) {
|
||||
event.preventDefault();
|
||||
const ajaxUrl = $('.js-delete-import-match').attr('data-url');
|
||||
const $dataMatchesDropdown = this.matchesDropdown;
|
||||
const selectedMatchId = $dataMatchesDropdown.val();
|
||||
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: ajaxUrl,
|
||||
data: {
|
||||
import_match_id: selectedMatchId,
|
||||
},
|
||||
}).then(() => {
|
||||
// Delete the match option from matches dropdown
|
||||
$dataMatchesDropdown.find(`option[value=${selectedMatchId}]`).remove();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new option to given dropdown.
|
||||
*
|
||||
* @param {jQuery} $dropdown
|
||||
* @param {String} optionText
|
||||
* @param {String} optionValue
|
||||
* @private
|
||||
*/
|
||||
appendOptionToDropdown($dropdown, optionText, optionValue) {
|
||||
const $newOption = $('<option>');
|
||||
|
||||
$newOption.attr('value', optionValue);
|
||||
$newOption.text(optionText);
|
||||
|
||||
$dropdown.append($newOption);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows error messages in the native error pop-up.
|
||||
*
|
||||
* @param {Array} errors
|
||||
* @private
|
||||
*/
|
||||
showErrorPopUp(errors) {
|
||||
alert(errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the matches dropdown.
|
||||
*
|
||||
* @returns {*|HTMLElement}
|
||||
*/
|
||||
get matchesDropdown() {
|
||||
return $('#matches');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "rows to skip" input.
|
||||
*
|
||||
* @returns {*|HTMLElement}
|
||||
*/
|
||||
get rowsSkipInput() {
|
||||
return $('#skip');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* 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 class ImportProgressModal {
|
||||
/**
|
||||
* Show the import progress modal window.
|
||||
*/
|
||||
show() {
|
||||
this.progressModal.modal('show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the import progress modal window.
|
||||
*/
|
||||
hide() {
|
||||
this.progressModal.modal('hide');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the import progressbar.
|
||||
*
|
||||
* @param {number} completed number of completed items.
|
||||
* @param {number} total number of items in total.
|
||||
*/
|
||||
updateProgress(completed, total) {
|
||||
const progressCompleted = parseInt(completed, 10);
|
||||
const progressTotal = parseInt(total, 10);
|
||||
const $progressBar = this.progressBar;
|
||||
const percentage = (progressCompleted / progressTotal) * 100;
|
||||
|
||||
$progressBar.css('width', `${percentage}%`);
|
||||
$progressBar.find('> span').text(`${progressCompleted}/${progressTotal}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the progress bar label.
|
||||
*
|
||||
* @param {String} label if not provided - will use the default label
|
||||
*/
|
||||
updateProgressLabel(label) {
|
||||
this.progressLabel.text(label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress label to "importing"
|
||||
*/
|
||||
setImportingProgressLabel() {
|
||||
this.updateProgressLabel(this.progressModal.find('.modal-body').data('importing-label'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress label to "imported"
|
||||
*/
|
||||
setImportedProgressLabel() {
|
||||
this.updateProgressLabel(this.progressModal.find('.modal-body').data('imported-label'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows information messages in the import modal.
|
||||
*
|
||||
* @param {Array} messages
|
||||
*/
|
||||
showInfoMessages(messages) {
|
||||
this.showMessages(this.infoMessageBlock, messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows warning messages in the import modal.
|
||||
*
|
||||
* @param {Array} messages
|
||||
*/
|
||||
showWarningMessages(messages) {
|
||||
this.showMessages(this.warningMessageBlock, messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows error messages in the import modal.
|
||||
*
|
||||
* @param {Array} messages
|
||||
*/
|
||||
showErrorMessages(messages) {
|
||||
this.showMessages(this.errorMessageBlock, messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the import success message.
|
||||
*/
|
||||
showSuccessMessage() {
|
||||
this.successMessageBlock.removeClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the post size limit warning message.
|
||||
*
|
||||
* @param {number} postSizeValue to be shown in the warning
|
||||
*/
|
||||
showPostLimitMessage(postSizeValue) {
|
||||
this.postLimitMessage.find('#post_limit_value').text(postSizeValue);
|
||||
this.postLimitMessage.removeClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show messages in given message block.
|
||||
*
|
||||
* @param {jQuery} $messageBlock
|
||||
* @param {Array} messages
|
||||
* @private
|
||||
*/
|
||||
showMessages($messageBlock, messages) {
|
||||
let showMessagesBlock = false;
|
||||
|
||||
Object.values(messages).forEach((msg) => {
|
||||
// Indicate that the messages block should be displayed
|
||||
showMessagesBlock = true;
|
||||
|
||||
const message = $('<div>');
|
||||
message.text(msg);
|
||||
message.addClass('message');
|
||||
|
||||
$messageBlock.append(message);
|
||||
});
|
||||
|
||||
if (showMessagesBlock) {
|
||||
$messageBlock.removeClass('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the "Ignore warnings and continue" button.
|
||||
*/
|
||||
showContinueImportButton() {
|
||||
this.continueImportButton.removeClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the "Ignore warnings and continue" button.
|
||||
*/
|
||||
hideContinueImportButton() {
|
||||
this.continueImportButton.addClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the "Abort import" button.
|
||||
*/
|
||||
showAbortImportButton() {
|
||||
this.abortImportButton.removeClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the "Abort import" button.
|
||||
*/
|
||||
hideAbortImportButton() {
|
||||
this.abortImportButton.addClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the "Close" button of the modal.
|
||||
*/
|
||||
showCloseModalButton() {
|
||||
this.closeModalButton.removeClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the "Close" button.
|
||||
*/
|
||||
hideCloseModalButton() {
|
||||
this.closeModalButton.addClass('d-none');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all warning messages from the modal.
|
||||
*/
|
||||
clearWarningMessages() {
|
||||
this.warningMessageBlock.addClass('d-none').find('.message').remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the modal - resets progress bar and removes messages.
|
||||
*/
|
||||
reset() {
|
||||
// Reset the progress bar
|
||||
this.updateProgress(0, 0);
|
||||
this.updateProgressLabel(this.progressLabel.attr('default-value'));
|
||||
|
||||
// Hide action buttons
|
||||
this.continueImportButton.addClass('d-none');
|
||||
this.abortImportButton.addClass('d-none');
|
||||
this.closeModalButton.addClass('d-none');
|
||||
|
||||
// Remove messages
|
||||
this.successMessageBlock.addClass('d-none');
|
||||
this.infoMessageBlock.addClass('d-none').find('.message').remove();
|
||||
this.errorMessageBlock.addClass('d-none').find('.message').remove();
|
||||
this.postLimitMessage.addClass('d-none');
|
||||
this.clearWarningMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets import progress modal.
|
||||
*
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
get progressModal() {
|
||||
return $('#import_progress_modal');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets import progress bar.
|
||||
*
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
get progressBar() {
|
||||
return this.progressModal.find('.progress-bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information messages block.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get infoMessageBlock() {
|
||||
return $('.js-import-info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets error messages block.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get errorMessageBlock() {
|
||||
return $('.js-import-errors');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets warning messages block.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get warningMessageBlock() {
|
||||
return $('.js-import-warnings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets success messages block.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get successMessageBlock() {
|
||||
return $('.js-import-success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets post limit message.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get postLimitMessage() {
|
||||
return $('.js-post-limit-warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets "Ignore warnings and continue" button.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get continueImportButton() {
|
||||
return $('.js-continue-import');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets "Abort import" button.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get abortImportButton() {
|
||||
return $('.js-abort-import');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets "Close" button of the modal.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get closeModalButton() {
|
||||
return $('.js-close-modal');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets progress bar label.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement}
|
||||
*/
|
||||
get progressLabel() {
|
||||
return $('#import_progress_bar').find('.progress-details-text');
|
||||
}
|
||||
}
|
||||
302
admin-kalsport/themes/new-theme/js/pages/import-data/Importer.js
Normal file
302
admin-kalsport/themes/new-theme/js/pages/import-data/Importer.js
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* 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 ImportProgressModal from './ImportProgressModal';
|
||||
import ImportBatchSizeCalculator from './ImportBatchSizeCalculator';
|
||||
import PostSizeChecker from './PostSizeChecker';
|
||||
|
||||
export default class Importer {
|
||||
constructor() {
|
||||
this.configuration = {};
|
||||
this.progressModal = new ImportProgressModal();
|
||||
this.batchSizeCalculator = new ImportBatchSizeCalculator();
|
||||
this.postSizeChecker = new PostSizeChecker();
|
||||
|
||||
// Default number of rows in one batch of the import.
|
||||
this.defaultBatchSize = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the import.
|
||||
*
|
||||
* @param {String} importUrl url of the controller, processing the import.
|
||||
* @param {Object} configuration import configuration.
|
||||
*/
|
||||
import(importUrl, configuration) {
|
||||
this.mergeConfiguration(configuration);
|
||||
this.importUrl = importUrl;
|
||||
|
||||
// Total number of rows to be imported.
|
||||
this.totalRowsCount = 0;
|
||||
|
||||
// Flags that mark that there were warnings or errors during import.
|
||||
this.hasWarnings = false;
|
||||
this.hasErrors = false;
|
||||
|
||||
// Resetting the import progress modal and showing it.
|
||||
this.progressModal.reset();
|
||||
this.progressModal.show();
|
||||
|
||||
// Starting the import with default batch size, which is adjusted for next iterations.
|
||||
this.ajaxImport(0, this.defaultBatchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the ajax import request.
|
||||
*
|
||||
* @param {number} offset row number, from which the import job will start processing data.
|
||||
* @param {number} batchSize batch size of this import job.
|
||||
* @param {boolean} validateOnly whether the data should be only validated, if false - the data will be imported.
|
||||
* @param {Object} recurringVariables variables which are recurring between import batch jobs.
|
||||
* @private
|
||||
*/
|
||||
ajaxImport(offset, batchSize, validateOnly = true, recurringVariables = {}) {
|
||||
this.mergeConfiguration({
|
||||
offset,
|
||||
limit: batchSize,
|
||||
validateOnly: validateOnly ? 1 : 0,
|
||||
crossStepsVars: JSON.stringify(recurringVariables),
|
||||
});
|
||||
|
||||
this.onImportStart();
|
||||
|
||||
$.post({
|
||||
url: this.importUrl,
|
||||
dataType: 'json',
|
||||
data: this.configuration,
|
||||
success: (response) => {
|
||||
if (this.importCancelRequested) {
|
||||
this.cancelImport();
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasErrors = response.errors && response.errors.length;
|
||||
const hasWarnings = response.warnings && response.warnings.length;
|
||||
const hasNotices = response.notices && response.notices.length;
|
||||
|
||||
if (response.totalCount !== undefined && response.totalCount) {
|
||||
// The total rows count is retrieved only in the first batch response.
|
||||
this.totalRowsCount = response.totalCount;
|
||||
}
|
||||
|
||||
// Update import progress.
|
||||
this.progressModal.updateProgress(response.doneCount, this.totalRowsCount);
|
||||
|
||||
if (!validateOnly) {
|
||||
// Set the progress label to "Importing".
|
||||
this.progressModal.setImportingProgressLabel();
|
||||
}
|
||||
|
||||
// Information messages are not shown during validation.
|
||||
if (!validateOnly && hasNotices) {
|
||||
this.progressModal.showInfoMessages(response.notices);
|
||||
}
|
||||
|
||||
if (hasErrors) {
|
||||
this.hasErrors = true;
|
||||
this.progressModal.showErrorMessages(response.errors);
|
||||
|
||||
// If there are errors and it's not validation step - stop the import.
|
||||
// If it's validation step - we will show all errors once it finishes.
|
||||
if (!validateOnly) {
|
||||
this.onImportStop();
|
||||
return false;
|
||||
}
|
||||
} else if (hasWarnings) {
|
||||
this.hasWarnings = true;
|
||||
this.progressModal.showWarningMessages(response.warnings);
|
||||
}
|
||||
|
||||
if (!response.isFinished) {
|
||||
// Marking the end of import operation.
|
||||
this.batchSizeCalculator.markImportEnd();
|
||||
|
||||
// Calculate next import batch size and offset.
|
||||
const nextOffset = offset + batchSize;
|
||||
const nextBatchSize = this.batchSizeCalculator.calculateBatchSize(batchSize, this.totalRowsCount);
|
||||
|
||||
// Showing a warning if post size limit is about to be reached.
|
||||
if (this.postSizeChecker.isReachingPostSizeLimit(response.postSizeLimit, response.nextPostSize)) {
|
||||
this.progressModal.showPostLimitMessage(
|
||||
this.postSizeChecker.getRequiredPostSizeInMegabytes(response.nextPostSize),
|
||||
);
|
||||
}
|
||||
|
||||
// Run the import again for the next batch.
|
||||
return this.ajaxImport(
|
||||
nextOffset,
|
||||
nextBatchSize,
|
||||
validateOnly,
|
||||
response.crossStepsVariables,
|
||||
);
|
||||
}
|
||||
|
||||
// All import batches are finished successfully.
|
||||
// If it was only validating the import data until this point,
|
||||
// we have to run the data import now.
|
||||
if (validateOnly) {
|
||||
// If errors occurred during validation - stop the import.
|
||||
if (this.hasErrors) {
|
||||
this.onImportStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.hasWarnings) {
|
||||
// Show the button to ignore warnings.
|
||||
this.progressModal.showContinueImportButton();
|
||||
this.onImportStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the progress bar to 100%.
|
||||
this.progressModal.updateProgress(this.totalRowsCount, this.totalRowsCount);
|
||||
|
||||
// Continue with the data import.
|
||||
return this.ajaxImport(0, this.defaultBatchSize, false);
|
||||
}
|
||||
|
||||
// Import is completely finished.
|
||||
return this.onImportFinish();
|
||||
},
|
||||
error: (XMLHttpRequest, textStatus) => {
|
||||
let txt = textStatus;
|
||||
|
||||
if (txt === 'parsererror') {
|
||||
txt = 'Technical error: Unexpected response returned by server. Import stopped.';
|
||||
}
|
||||
|
||||
this.onImportStop();
|
||||
this.progressModal.showErrorMessages([txt]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Continue the import when it was stopped.
|
||||
*/
|
||||
continueImport() {
|
||||
if (!this.configuration) {
|
||||
throw new Error('Missing import configuration. Make sure the import had started before continuing.');
|
||||
}
|
||||
|
||||
this.progressModal.hideContinueImportButton();
|
||||
this.progressModal.hideCloseModalButton();
|
||||
this.progressModal.clearWarningMessages();
|
||||
this.ajaxImport(0, this.defaultBatchSize, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the import configuration.
|
||||
*
|
||||
* @param importConfiguration
|
||||
*/
|
||||
set configuration(importConfiguration) {
|
||||
this.importConfiguration = importConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import configuration.
|
||||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
get configuration() {
|
||||
return this.importConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set progress modal.
|
||||
*
|
||||
* @param {ImportProgressModal} modal
|
||||
*/
|
||||
set progressModal(modal) {
|
||||
this.modal = modal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get progress modal.
|
||||
*
|
||||
* @returns {ImportProgressModal}
|
||||
*/
|
||||
get progressModal() {
|
||||
return this.modal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request import cancellation.
|
||||
* Import operation will be cancelled at next iteration when requested.
|
||||
*/
|
||||
requestCancelImport() {
|
||||
this.importCancelRequested = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge given configuration into current import configuration.
|
||||
*
|
||||
* @param {Object} configuration
|
||||
* @private
|
||||
*/
|
||||
mergeConfiguration(configuration) {
|
||||
this.importConfiguration = {...this.importConfiguration, ...configuration};
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the import process.
|
||||
* @private
|
||||
*/
|
||||
cancelImport() {
|
||||
this.progressModal.hide();
|
||||
this.importCancelRequested = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional actions when import is stopped.
|
||||
* @private
|
||||
*/
|
||||
onImportStop() {
|
||||
this.progressModal.showCloseModalButton();
|
||||
this.progressModal.hideAbortImportButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional actions when import is finished.
|
||||
* @private
|
||||
*/
|
||||
onImportFinish() {
|
||||
this.onImportStop();
|
||||
this.progressModal.showSuccessMessage();
|
||||
this.progressModal.setImportedProgressLabel();
|
||||
this.progressModal.updateProgress(this.totalRowsCount, this.totalRowsCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional actions when import is starting.
|
||||
* @private
|
||||
*/
|
||||
onImportStart() {
|
||||
// Marking the start of import operation.
|
||||
this.batchSizeCalculator.markImportStart();
|
||||
this.progressModal.showAbortImportButton();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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 class PostSizeChecker {
|
||||
constructor() {
|
||||
// How close can we get to the post size limit. 0.9 means 90%.
|
||||
this.postSizeLimitThreshold = 0.9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given postSizeLimit is reaching the required post size
|
||||
*
|
||||
* @param {number} postSizeLimit
|
||||
* @param {number} requiredPostSize
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isReachingPostSizeLimit(postSizeLimit, requiredPostSize) {
|
||||
return requiredPostSize >= postSizeLimit * this.postSizeLimitThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required post size in megabytes.
|
||||
*
|
||||
* @param {number} requiredPostSize
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
getRequiredPostSizeInMegabytes(requiredPostSize) {
|
||||
return parseInt(requiredPostSize / (1024 * 1024), 10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 ImportDataPage from './ImportDataPage';
|
||||
|
||||
const {$} = window;
|
||||
|
||||
$(() => {
|
||||
new ImportDataPage();
|
||||
});
|
||||
Reference in New Issue
Block a user