Paste functionality
[MAILPOET-1808]
This commit is contained in:
@@ -81,17 +81,7 @@ jQuery(document).ready(() => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
// render process button for each method
|
|
||||||
const methodProcessContainerTemplate = Handlebars.compile(jQuery('#method_process_template').html());
|
|
||||||
jQuery('.mailpoet_method_process').html(methodProcessContainerTemplate());
|
|
||||||
|
|
||||||
// define reusable variables
|
|
||||||
const currentStepE = jQuery(window.location.hash);
|
|
||||||
const methodSelectionElement = jQuery('#select_method');
|
|
||||||
const pasteInputElement = jQuery('#paste_input');
|
|
||||||
const pasteInputPlaceholderElement = pasteInputElement.data('placeholder').replace(/\\n/g, '\n');
|
|
||||||
const pasteProcessButtonElement = jQuery('#method_paste > div.mailpoet_method_process')
|
|
||||||
.find('a.mailpoet_process');
|
|
||||||
const mailChimpKeyInputElement = jQuery('#mailchimp_key');
|
const mailChimpKeyInputElement = jQuery('#mailchimp_key');
|
||||||
const mailChimpKeyVerifyButtonElement = jQuery('#mailchimp_key_verify');
|
const mailChimpKeyVerifyButtonElement = jQuery('#mailchimp_key_verify');
|
||||||
const mailChimpListsContainerElement = jQuery('#mailchimp_lists');
|
const mailChimpListsContainerElement = jQuery('#mailchimp_lists');
|
||||||
@@ -101,35 +91,6 @@ jQuery(document).ready(() => {
|
|||||||
const uploadProcessButtonElement = jQuery('#method_file > div.mailpoet_method_process')
|
const uploadProcessButtonElement = jQuery('#method_file > div.mailpoet_method_process')
|
||||||
.find('a.mailpoet_process');
|
.find('a.mailpoet_process');
|
||||||
|
|
||||||
// define method change behavior
|
|
||||||
methodSelectionElement.change(() => {
|
|
||||||
const availableMethods = jQuery(':radio[name="select_method"]');
|
|
||||||
const selectedMethod = availableMethods.index(availableMethods.filter(':checked'));
|
|
||||||
MailPoet.Notice.hide();
|
|
||||||
// hide all methods
|
|
||||||
currentStepE.find('.inside')
|
|
||||||
.children('div[id^="method_"]')
|
|
||||||
.hide();
|
|
||||||
// show selected method
|
|
||||||
currentStepE.find('.inside')
|
|
||||||
.children(`div[id^="method_"]:eq(${selectedMethod})`)
|
|
||||||
.show()
|
|
||||||
.find('table')
|
|
||||||
.show();
|
|
||||||
});
|
|
||||||
|
|
||||||
// start step 1
|
|
||||||
showCurrentStep();
|
|
||||||
|
|
||||||
function toggleNextStepButton(element, condition) {
|
|
||||||
const disabled = 'button-disabled';
|
|
||||||
if (condition === 'on') {
|
|
||||||
element.closest('table a').removeClass(disabled);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
element.closest('table a').addClass(disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
function papaParserConfig(isFile) {
|
function papaParserConfig(isFile) {
|
||||||
return {
|
return {
|
||||||
skipEmptyLines: true,
|
skipEmptyLines: true,
|
||||||
@@ -191,47 +152,6 @@ jQuery(document).ready(() => {
|
|||||||
mailChimpListsContainerElement.show();
|
mailChimpListsContainerElement.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Paste
|
|
||||||
*/
|
|
||||||
pasteInputElement
|
|
||||||
.attr('value', pasteInputPlaceholderElement).css('color', '#999')
|
|
||||||
.focus((event) => {
|
|
||||||
if (jQuery(event.currentTarget).val() === pasteInputPlaceholderElement) {
|
|
||||||
jQuery(event.currentTarget).attr('value', '').css('color', '#222');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.blur((event) => {
|
|
||||||
if (jQuery(event.currentTarget).val() === '') {
|
|
||||||
jQuery(event.currentTarget).attr('value', pasteInputPlaceholderElement).css('color', '#999');
|
|
||||||
}
|
|
||||||
toggleNextStepButton(
|
|
||||||
pasteProcessButtonElement,
|
|
||||||
(event.currentTarget.value.trim() !== '') ? 'on' : 'off'
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.keyup((event) => {
|
|
||||||
toggleNextStepButton(
|
|
||||||
pasteProcessButtonElement,
|
|
||||||
(event.currentTarget.value.trim() !== '') ? 'on' : 'off'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
pasteProcessButtonElement.click(() => {
|
|
||||||
const pasteSize = encodeURI(pasteInputElement.val()).split(/%..|./).length - 1;
|
|
||||||
MailPoet.Notice.hide();
|
|
||||||
// get an approximate size of textarea paste in bytes
|
|
||||||
if (pasteSize > window.maxPostSizeBytes) {
|
|
||||||
MailPoet.Notice.error(MailPoet.I18n.t('maxPostSizeNotice'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// delay loading indicator for 10ms or else it's just too fast :)
|
|
||||||
MailPoet.Modal.loading(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
Papa.parse(pasteInputElement.val(), papaParserConfig(false));
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CSV file
|
* CSV file
|
||||||
*/
|
*/
|
||||||
|
@@ -1,16 +1,35 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import MailPoet from 'mailpoet';
|
||||||
|
import Papa from 'papaparse';
|
||||||
import PreviousNextStepButtons from './previous_next_step_buttons.jsx';
|
import PreviousNextStepButtons from './previous_next_step_buttons.jsx';
|
||||||
import SelectMethod from './step_method_selection/select_import_method.jsx';
|
import SelectMethod from './step_method_selection/select_import_method.jsx';
|
||||||
import MethodPaste from './step_method_selection/method_paste.jsx';
|
import MethodPaste from './step_method_selection/method_paste.jsx';
|
||||||
import MethodUpload from './step_method_selection/method_upload.jsx';
|
import MethodUpload from './step_method_selection/method_upload.jsx';
|
||||||
import MethodMailChimp from './step_method_selection/method_mailchimp.jsx';
|
import MethodMailChimp from './step_method_selection/method_mailchimp.jsx';
|
||||||
|
import sanitizeCSVData from './sanitize_csv_data.jsx';
|
||||||
|
|
||||||
|
const SUBSCRIBERS_LIMIT_FOR_VALIDATION = 500;
|
||||||
|
|
||||||
|
const getNextStepLink = (importData) => {
|
||||||
|
if (importData === undefined) {
|
||||||
|
return 'step_data_manipulation';
|
||||||
|
}
|
||||||
|
if (importData.subscribersCount === undefined) {
|
||||||
|
return 'step_data_manipulation';
|
||||||
|
}
|
||||||
|
if (importData.subscribersCount < SUBSCRIBERS_LIMIT_FOR_VALIDATION) {
|
||||||
|
return 'step_data_manipulation';
|
||||||
|
}
|
||||||
|
return 'step_input_validation';
|
||||||
|
};
|
||||||
|
|
||||||
function StepMethodSelection({
|
function StepMethodSelection({
|
||||||
navigate,
|
navigate,
|
||||||
}) {
|
}) {
|
||||||
const [canGoNext, setCanGoNext] = useState(false);
|
const [canGoNext, setCanGoNext] = useState(false);
|
||||||
const [method, setMethod] = useState(undefined);
|
const [method, setMethod] = useState(undefined);
|
||||||
|
const [csvData, setCsvData] = useState('');
|
||||||
|
|
||||||
const setInputValid = () => {
|
const setInputValid = () => {
|
||||||
setCanGoNext(true);
|
setCanGoNext(true);
|
||||||
@@ -20,13 +39,58 @@ function StepMethodSelection({
|
|||||||
setCanGoNext(false);
|
setCanGoNext(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function papaParserConfig(isFile) {
|
||||||
|
return {
|
||||||
|
skipEmptyLines: true,
|
||||||
|
error() {
|
||||||
|
MailPoet.Notice.hide();
|
||||||
|
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'));
|
||||||
|
},
|
||||||
|
complete(CSV) {
|
||||||
|
const sanitizedData = sanitizeCSVData(CSV.data);
|
||||||
|
if (sanitizedData) {
|
||||||
|
// since we assume that the header line is always present, we need
|
||||||
|
// to detect the header by checking if it contains a valid e-mail address
|
||||||
|
window.importData.step_method_selection = sanitizedData;
|
||||||
|
MailPoet.trackEvent('Subscribers import started', {
|
||||||
|
source: isFile ? 'file upload' : 'pasted data',
|
||||||
|
'MailPoet Free version': window.mailpoet_version,
|
||||||
|
});
|
||||||
|
navigate(
|
||||||
|
getNextStepLink(window.importData.step_method_selection),
|
||||||
|
{ trigger: true }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
MailPoet.Modal.loading(false);
|
||||||
|
let errorNotice = MailPoet.I18n.t('noValidRecords');
|
||||||
|
errorNotice = errorNotice.replace('[link]', MailPoet.I18n.t('csvKBLink'));
|
||||||
|
errorNotice = errorNotice.replace('[/link]', '</a>');
|
||||||
|
MailPoet.Notice.error(errorNotice);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = () => {
|
||||||
|
const pasteSize = encodeURI(csvData).split(/%..|./).length - 1;
|
||||||
|
MailPoet.Notice.hide();
|
||||||
|
// get an approximate size of textarea paste in bytes
|
||||||
|
if (pasteSize > window.maxPostSizeBytes) {
|
||||||
|
MailPoet.Notice.error(MailPoet.I18n.t('maxPostSizeNotice'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// delay loading indicator for 10ms or else it's just too fast :)
|
||||||
|
MailPoet.Modal.loading(true);
|
||||||
|
Papa.parse(csvData, papaParserConfig(false));
|
||||||
|
};
|
||||||
|
|
||||||
const showNextButton = () => {
|
const showNextButton = () => {
|
||||||
if (method) {
|
if (method) {
|
||||||
return (
|
return (
|
||||||
<PreviousNextStepButtons
|
<PreviousNextStepButtons
|
||||||
canGoNext={canGoNext}
|
canGoNext={canGoNext}
|
||||||
hidePrevious
|
hidePrevious
|
||||||
onNextAction={() => navigate('step_data_manipulation', { trigger: true })}
|
onNextAction={process}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -42,6 +106,7 @@ function StepMethodSelection({
|
|||||||
{ method === 'paste-method'
|
{ method === 'paste-method'
|
||||||
? (
|
? (
|
||||||
<MethodPaste
|
<MethodPaste
|
||||||
|
onValueChange={setCsvData}
|
||||||
setInputValid={setInputValid}
|
setInputValid={setInputValid}
|
||||||
setInputInvalid={setInputInValid}
|
setInputInvalid={setInputInValid}
|
||||||
/>
|
/>
|
||||||
|
@@ -7,13 +7,14 @@ const kbLink = 'http://docs.mailpoet.com/article/126-importing-subscribers-with-
|
|||||||
|
|
||||||
const placeholder = 'Email, First Name, Last Name\njohn@doe.com, John, Doe\nmary@smith.com, Mary, Smith\njohnny@walker.com, Johnny, Walker';
|
const placeholder = 'Email, First Name, Last Name\njohn@doe.com, John, Doe\nmary@smith.com, Mary, Smith\njohnny@walker.com, Johnny, Walker';
|
||||||
|
|
||||||
const MethodPaste = ({ setInputValid, setInputInvalid }) => {
|
const MethodPaste = ({ setInputValid, setInputInvalid, onValueChange }) => {
|
||||||
const onChange = (e) => {
|
const onChange = (e) => {
|
||||||
if (e.target.value) {
|
if (e.target.value) {
|
||||||
setInputValid();
|
setInputValid();
|
||||||
} else {
|
} else {
|
||||||
setInputInvalid();
|
setInputInvalid();
|
||||||
}
|
}
|
||||||
|
onValueChange(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -53,6 +54,7 @@ const MethodPaste = ({ setInputValid, setInputInvalid }) => {
|
|||||||
MethodPaste.propTypes = {
|
MethodPaste.propTypes = {
|
||||||
setInputValid: PropTypes.func,
|
setInputValid: PropTypes.func,
|
||||||
setInputInvalid: PropTypes.func,
|
setInputInvalid: PropTypes.func,
|
||||||
|
onValueChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
MethodPaste.defaultProps = {
|
MethodPaste.defaultProps = {
|
||||||
|
Reference in New Issue
Block a user