Add warnings to manipulation step

[MAILPOET-1809]
This commit is contained in:
Pavel Dohnal
2019-07-02 15:28:50 +02:00
committed by M. Shull
parent 729efbb62f
commit 4b6b293eef
6 changed files with 128 additions and 160 deletions

View File

@@ -11,97 +11,19 @@ import StepInputValidation from './import/step_input_validation.jsx';
import StepMethodSelection from './import/step_method_selection.jsx';
import StepResults from './import/step_results.jsx';
const SUBSCRIBERS_LIMIT_FOR_VALIDATION = 500;
function getDataManipulationPreviousStepLink(importData) {
if (importData === undefined) {
return 'step_method_selection';
}
if (importData.subscribersCount === undefined) {
return 'step_method_selection';
}
if (importData.subscribersCount < SUBSCRIBERS_LIMIT_FOR_VALIDATION) {
return 'step_method_selection';
}
return 'step_input_validation';
}
jQuery(document).ready(() => {
if (!jQuery('#mailpoet_subscribers_import').length) {
return;
}
jQuery('input[name="select_method"]').attr('checked', false);
// configure router
const router = new (Backbone.Router.extend({
routes: {
'': 'home',
step_method_selection: 'step_method_selection',
step_input_validation: 'step_input_validation',
step_data_manipulation: 'step_data_manipulation',
step_results: 'step_results',
},
home() {
this.navigate('step_method_selection', { trigger: true });
},
}))();
function showCurrentStep() {
MailPoet.Notice.hide();
MailPoet.Modal.loading(false);
jQuery('#mailpoet_subscribers_import > div[id^="step"]').hide();
jQuery(window.location.hash).show();
}
router.on('route:step_method_selection', () => {
showCurrentStep();
const container = document.getElementById('step_method_selection');
if (container) {
ReactDOM.render(
<StepMethodSelection
navigate={router.navigate}
/>,
container
);
}
});
router.on('route:step_input_validation', () => {
if (typeof (window.importData.step_method_selection) === 'undefined') {
router.navigate('step_method_selection', { trigger: true });
return;
}
showCurrentStep();
const container = document.getElementById('step_input_validation');
if (container) {
ReactDOM.render(
<StepInputValidation
navigate={router.navigate}
importData={window.importData.step_method_selection}
/>,
container
);
}
});
router.on('route:step_data_manipulation', () => {
let fillerPosition;
let importResults;
let duplicates;
if (typeof (window.importData.step_method_selection) === 'undefined') {
router.navigate('step_method_selection', { trigger: true });
return;
}
// define reusable variables
const nextStepButton = jQuery('#next_step');
const previousStepButton = jQuery('#return_to_previous');
// create a copy of subscribers object for further manipulation
const subscribers = jQuery.extend(true, {}, window.importData.step_method_selection);
const subscribersDataTemplate = Handlebars.compile(jQuery('#subscribers_data_template').html());
const subscribersDataTemplatePartial = Handlebars.compile(jQuery('#subscribers_data_template_partial').html());
const subscribersDataParseResultsTemplate = Handlebars.compile(jQuery('#subscribers_data_parse_results_template').html());
const segmentSelectElement = jQuery('#mailpoet_segments_select');
const maxRowsToShow = 10;
const filler = '. . .';
@@ -110,8 +32,6 @@ jQuery(document).ready(() => {
const fillerArray = Array(...new Array(subscribers.subscribers[0].length))
.map(String.prototype.valueOf, filler);
showCurrentStep();
function toggleNextStepButton(condition) {
const disabled = 'button-disabled';
if (condition === 'on') {
@@ -125,61 +45,6 @@ jQuery(document).ready(() => {
jQuery('#subscribers_data_parse_results:visible').html('');
jQuery('#subscribers_data_import_results:visible').hide();
// show parse statistics if any duplicate/invalid records were found
if (subscribers.invalid.length || subscribers.duplicate.length || subscribers.role.length) {
// count repeating e-mails inside duplicate array and present them in
// 'email (xN)' format
duplicates = {};
subscribers.duplicate.forEach((subscriberEmail) => {
duplicates[subscriberEmail] = (duplicates[subscriberEmail] || 0) + 1;
});
subscribers.duplicate = [];
Object.keys(duplicates).forEach((email) => {
if (duplicates[email] > 1) {
subscribers.duplicate.push(`${email} (x${duplicates[email]})`);
} else {
subscribers.duplicate.push(email);
}
});
importResults = {
notice: MailPoet.I18n.t('importNoticeSkipped').replace(
'%1$s',
`<strong>${subscribers.invalid.length + subscribers.duplicate.length + subscribers.role.length}</strong>`
),
invalid: (subscribers.invalid.length)
? MailPoet.I18n.t('importNoticeInvalid')
.replace('%1$s', `<strong>${subscribers.invalid.length.toLocaleString()}</strong>`)
.replace('%2$s', subscribers.invalid.join(', '))
: null,
duplicate: (subscribers.duplicate.length)
? MailPoet.I18n.t('importNoticeDuplicate')
.replace('%1$s', `<strong>${subscribers.duplicate.length}</strong>`)
.replace('%2$s', subscribers.duplicate.join(', '))
: null,
role: (subscribers.role.length)
? MailPoet.I18n.t('importNoticeRoleBased')
.replace('%1$s', `<strong>${subscribers.role.length.toLocaleString()}</strong>`)
.replace('%2$s', subscribers.role.join(', '))
.replace(
/\[link](.+)\[\/link]/,
'<a href="https://kb.mailpoet.com/article/270-role-based-email-addresses-are-not-allowed" target="_blank" rel="noopener noreferrer">$1</a>'
)
: null,
};
jQuery('#subscribers_data_parse_results').html(
subscribersDataParseResultsTemplate(importResults)
);
}
jQuery('.mailpoet_subscribers_data_parse_results_details_show')
.click(function detailsClick() {
const details = jQuery('.mailpoet_subscribers_data_parse_results_details');
details.toggle();
jQuery(this).text((details.is(':visible'))
? MailPoet.I18n.t('hideDetails')
: MailPoet.I18n.t('showDetails'));
});
// show available segments
if (window.mailpoetSegments.length) {
@@ -610,12 +475,6 @@ jQuery(document).ready(() => {
filterSubscribers();
});
previousStepButton.off().on('click', () => {
router.navigate(
getDataManipulationPreviousStepLink(window.importData.step_method_selection),
{ trigger: true }
);
});
nextStepButton.off().on('click', (event) => {
const columns = {};

View File

@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import PreviousNextStepButtons from './previous_next_step_buttons.jsx';
import Warnings from './step_data_manipulation/warnings.jsx';
function getPreviousStepLink(importData, subscribersLimitForValidation) {
if (importData === undefined) {
@@ -32,6 +33,9 @@ function StepDataManipulation({
return (
<>
<Warnings
stepMethodSelectionData={stepMethodSelectionData}
/>
<PreviousNextStepButtons
canGoNext={false}
onPreviousAction={() => (

View File

@@ -0,0 +1,117 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import ReactStringReplace from 'react-string-replace';
import classNames from 'classnames';
const getSingleWarning = (warningTranslation, subscribers) => {
let warning = '';
if (subscribers.length) {
warning = ReactStringReplace(
warningTranslation.replace('%2$s', subscribers.join(', ')),
'%1$s',
() => <strong key={warningTranslation}>{subscribers.length.toLocaleString()}</strong>
);
warning = <p>{warning}</p>;
}
return warning;
};
const Warnings = ({
stepMethodSelectionData,
}) => {
const { invalid, duplicate, role } = stepMethodSelectionData;
const [detailsShown, setDetailsShown] = useState(false);
const detailClasses = classNames(
'mailpoet_subscribers_data_parse_results_details',
{ mailpoet_hidden: !detailsShown },
);
const invalidWarning = getSingleWarning(MailPoet.I18n.t('importNoticeInvalid'), invalid);
const duplicateWarning = getSingleWarning(MailPoet.I18n.t('importNoticeDuplicate'), duplicate);
let roleBasedWarning = '';
if (role.length) {
roleBasedWarning = ReactStringReplace(
MailPoet.I18n.t('importNoticeRoleBased'),
/(%1\$s|\[link\].*\[\/link\]|%2\$s)/,
(match) => {
if (match === '%1$s') return <strong key="role-length">{role.length.toLocaleString()}</strong>;
if (match === '%2$s') return role.join(', ');
return (
<a
href="https://kb.mailpoet.com/article/270-role-based-email-addresses-are-not-allowed"
target="_blank"
rel="noopener noreferrer"
key={match}
>
{match.replace('[link]', '').replace('[/link]', '')}
</a>
);
}
);
roleBasedWarning = <p>{roleBasedWarning}</p>;
}
if (
invalid.length
|| duplicate.length
|| role.length
) {
const allWarningsCount = invalid.length + duplicate.length + role.length;
return (
<div className="error">
<p>
{ReactStringReplace(MailPoet.I18n.t('importNoticeSkipped'), '%1$s', () => (
<strong key="lengths">{allWarningsCount.toLocaleString()}</strong>
))}
{' '}
<a
className="mailpoet_subscribers_data_parse_results_details_show"
data-automation-id="show-more-details"
onClick={() => setDetailsShown(!detailsShown)}
role="button"
tabIndex={0}
onKeyDown={(event) => {
if ((['keydown', 'keypress'].includes(event.type) && ['Enter', ' '].includes(event.key))
) {
event.preventDefault();
setDetailsShown(!detailsShown);
}
}}
>
{MailPoet.I18n.t('showMoreDetails')}
</a>
</p>
<div className={detailClasses}>
<hr />
{invalidWarning}
{duplicateWarning}
{roleBasedWarning}
</div>
</div>
);
}
return null;
};
Warnings.propTypes = {
stepMethodSelectionData: PropTypes.shape({
duplicate: PropTypes.arrayOf(PropTypes.string),
invalid: PropTypes.arrayOf(PropTypes.string),
role: PropTypes.arrayOf(PropTypes.string),
}),
};
Warnings.defaultProps = {
stepMethodSelectionData: {
invalid: [],
duplicate: [],
role: [],
},
};
export default Warnings;