Upload csv by file in React

[MAILPOET-1808]
This commit is contained in:
Pavel Dohnal
2019-04-11 14:12:22 +02:00
committed by M. Shull
parent bd5d62304d
commit 8d6b05a80e
7 changed files with 107 additions and 138 deletions

View File

@@ -87,41 +87,6 @@ jQuery(document).ready(() => {
const mailChimpListsContainerElement = jQuery('#mailchimp_lists');
const mailChimpProcessButtonElement = jQuery('#method_mailchimp > div.mailpoet_method_process')
.find('a.mailpoet_process');
const uploadElement = jQuery('#file_local');
const uploadProcessButtonElement = jQuery('#method_file > div.mailpoet_method_process')
.find('a.mailpoet_process');
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,
});
router.navigate(
getMethodSelectionNextStepLink(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);
}
},
};
}
function displayMailChimpLists(data) {
const listSelectElement = mailChimpListsContainerElement.find('select');
@@ -152,35 +117,6 @@ jQuery(document).ready(() => {
mailChimpListsContainerElement.show();
}
/*
* CSV file
*/
uploadElement.change((event) => {
const ext = event.currentTarget.value.match(/[^.]+$/);
MailPoet.Notice.hide();
if (ext === null || ext[0].toLowerCase() !== 'csv') {
event.currentTarget.value.val('');
MailPoet.Notice.error(MailPoet.I18n.t('wrongFileFormat'));
}
toggleNextStepButton(
uploadProcessButtonElement,
(event.currentTarget.value.trim() !== '') ? 'on' : 'off'
);
});
uploadProcessButtonElement.click(() => {
if (uploadElement.val().trim() !== '') {
// delay loading indicator for 10ms or else it's just too fast :)
MailPoet.Modal.loading(true);
setTimeout(() => {
uploadElement.parse({
config: papaParserConfig(true),
});
}, 10);
}
});
/*
* MailChimp
*/

View File

@@ -1,13 +1,12 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import Papa from 'papaparse';
import PreviousNextStepButtons from './previous_next_step_buttons.jsx';
import SelectMethod from './step_method_selection/select_import_method.jsx';
import MethodPaste from './step_method_selection/method_paste.jsx';
import MethodUpload from './step_method_selection/method_upload.jsx';
import MethodMailChimp from './step_method_selection/method_mailchimp.jsx';
import sanitizeCSVData from './sanitize_csv_data.jsx';
import processCsv from './step_method_selection/process_csv.jsx';
const SUBSCRIBERS_LIMIT_FOR_VALIDATION = 500;
@@ -39,49 +38,18 @@ function StepMethodSelection({
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));
processCsv(csvData, (sanitizedData) => {
window.importData.step_method_selection = sanitizedData;
MailPoet.trackEvent('Subscribers import started', {
source: method === 'file-method' ? 'file upload' : 'pasted data',
'MailPoet Free version': window.mailpoet_version,
});
navigate(
getNextStepLink(window.importData.step_method_selection),
{ trigger: true }
);
});
};
const showNextButton = () => {
@@ -112,10 +80,12 @@ function StepMethodSelection({
/>
) : null
}
{ method === 'csv-method'
{ method === 'file-method'
? (
<MethodUpload
onValueChange={setCsvData}
setInputValid={setInputValid}
setInputInvalid={setInputInValid}
/>
) : null
}

View File

@@ -1,21 +1,69 @@
import React from 'react';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import ReactStringReplace from 'react-string-replace';
const kbLink = 'http://docs.mailpoet.com/article/126-importing-subscribers-with-csv-files'
const MethodUpload = ({ setInputValid, setInputInvalid, onValueChange }) => {
const onChange = (e) => {
const ext = e.target.value.match(/[^.]+$/);
MailPoet.Notice.hide();
if (ext === null || ext[0].toLowerCase() !== 'csv') {
setInputInvalid();
MailPoet.Notice.error(MailPoet.I18n.t('wrongFileFormat'));
onValueChange('');
} else {
onValueChange(e.target.files[0]);
setInputValid();
}
};
const MethodUpload = ({ setInputValid }) => {
return (
<div>
MethodUpload
<>
<label htmlFor="paste_input" className="import_method_paste">
<div className="import_paste_texts">
<span className="import_heading">{MailPoet.I18n.t('methodUpload')}</span>
<p className="description">
{ReactStringReplace(
MailPoet.I18n.t('pasteDescription'),
/\[link\](.*?)\[\/link\]/,
match => (
<a
href={`${kbLink}`}
key="kb-link"
target="_blank"
rel="noopener noreferrer"
>
{ match }
</a>
)
)}
</p>
</div>
<input
type="file"
id="file_local"
accept=".csv"
data-automation-id="import-file-upload-input"
onChange={onChange}
/>
</label>
</>
</div>
);
};
MethodUpload.propTypes = {
setInputValid: PropTypes.func,
setInputInvalid: PropTypes.func,
onValueChange: PropTypes.func.isRequired,
};
MethodUpload.defaultProps = {
setInputValid: () => {},
setInputInvalid: () => {},
};
export default MethodUpload;

View File

@@ -0,0 +1,39 @@
import Papa from 'papaparse';
import MailPoet from 'mailpoet';
import sanitizeCSVData from '../sanitize_csv_data.jsx';
function papaParserConfig(done) {
return {
skipEmptyLines: true,
error() {
MailPoet.Notice.hide();
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'));
},
complete(CSV) {
const sanitizedData = sanitizeCSVData(CSV.data);
if (sanitizedData) {
done(sanitizedData);
} 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 = (csvData, done) => {
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;
}
MailPoet.Modal.loading(true);
Papa.parse(csvData, papaParserConfig(done));
};
export default process;

View File

@@ -28,8 +28,8 @@ function SelectImportMethod({
name="select_method"
data-automation-id="import-csv-method"
id="import-csv-method"
checked={activeMethod === 'csv-method'}
onChange={() => onMethodChange('csv-method')}
checked={activeMethod === 'file-method'}
onChange={() => onMethodChange('file-method')}
/>
{MailPoet.I18n.t('methodUpload')}
</label>

View File

@@ -69,7 +69,7 @@
</td>
</tr>
<tr>
<th>
<th style="width: 300px;">
<a href="javascript:;" id="return_to_previous"
class="button-primary wysija button"><%= __('Previous step') %> </a>
&nbsp;&nbsp;

View File

@@ -1,29 +1,5 @@
<div id="step_method_selection" class="mailpoet_hidden method_selection_step">
<div class="inside">
<!-- CSV file -->
<div id="method_file" class="mailpoet_hidden">
<table class="mailpoet_subscribers form-table">
<tbody>
<tr>
<th scope="row">
<label for="file_local">
<%= __('Upload a file') %>
<p class="description">
<%= csvDescription|replaceLinkTags(csvKBLink, {'target' : '_blank'})|raw %>
</p>
</label>
</th>
<td>
<input type="file" id="file_local" accept=".csv" data-automation-id="import-file-upload-input" />
</td>
</tr>
</tbody>
</table>
<div class="mailpoet_method_process">
<!-- Template data -->
</div>
</div>
<!-- Mailchimp -->
<div id="method_mailchimp" class="mailpoet_hidden">
<table class="mailpoet_subscribers form-table">