Add error boundary to subscriber import/export app
[MAILPOET-4706]
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
import { withBoundary } from './error_boundary';
|
||||||
|
|
||||||
function ScrollToTopComponent({ children, location: { pathname } }) {
|
function ScrollToTopComponent({ children, location: { pathname } }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -9,4 +10,5 @@ function ScrollToTopComponent({ children, location: { pathname } }) {
|
|||||||
return children || null;
|
return children || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ScrollToTop = withRouter(ScrollToTopComponent);
|
ScrollToTopComponent.displayName = 'ScrollToTopComponent';
|
||||||
|
export const ScrollToTop = withRouter(withBoundary(ScrollToTopComponent));
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import { FilterType, List, Logs } from './list';
|
|
||||||
import { ErrorBoundary } from 'common';
|
import { ErrorBoundary } from 'common';
|
||||||
|
import { FilterType, List, Logs } from './list';
|
||||||
|
|
||||||
interface LogsWindow extends Window {
|
interface LogsWindow extends Window {
|
||||||
mailpoet_logs: Logs;
|
mailpoet_logs: Logs;
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { GlobalContext } from 'context/index.jsx';
|
import { GlobalContext } from 'context/index.jsx';
|
||||||
|
import { withBoundary } from 'common';
|
||||||
import { Notice } from './notice.tsx';
|
import { Notice } from './notice.tsx';
|
||||||
|
|
||||||
const Notices = () => {
|
const NoticesComponent = () => {
|
||||||
const { notices } = useContext(GlobalContext);
|
const { notices } = useContext(GlobalContext);
|
||||||
return notices.items.map(({ id, ...props }) => (
|
return notices.items.map(({ id, ...props }) => (
|
||||||
<Notice key={id} {...props} />
|
<Notice key={id} {...props} />
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
Notices.displayName = 'Notices';
|
NoticesComponent.displayName = 'Notices';
|
||||||
|
const Notices = withBoundary(NoticesComponent);
|
||||||
export { Notices };
|
export { Notices };
|
||||||
|
@@ -5,6 +5,7 @@ import { ScrollToTop } from 'common/scroll_to_top.jsx';
|
|||||||
|
|
||||||
import { GlobalContext, useGlobalContextValue } from 'context/index.jsx';
|
import { GlobalContext, useGlobalContextValue } from 'context/index.jsx';
|
||||||
import { Notices } from 'notices/notices.jsx';
|
import { Notices } from 'notices/notices.jsx';
|
||||||
|
import { withBoundary } from 'common';
|
||||||
import { StepMethodSelection } from './import/step_method_selection.jsx';
|
import { StepMethodSelection } from './import/step_method_selection.jsx';
|
||||||
import { StepInputValidation } from './import/step_input_validation.jsx';
|
import { StepInputValidation } from './import/step_input_validation.jsx';
|
||||||
import { StepDataManipulation } from './import/step_data_manipulation.jsx';
|
import { StepDataManipulation } from './import/step_data_manipulation.jsx';
|
||||||
@@ -28,7 +29,7 @@ function ImportSubscribers() {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route
|
<Route
|
||||||
path="/step_clean_list"
|
path="/step_clean_list"
|
||||||
render={(props) => <StepCleanList {...props} />}
|
render={withBoundary(StepCleanList)}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/step_method_selection"
|
path="/step_method_selection"
|
||||||
|
@@ -5,7 +5,7 @@ type Props = {
|
|||||||
onProceed?: () => void;
|
onProceed?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CleanList({ onProceed }: Props): JSX.Element {
|
function CleanList({ onProceed }: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-clean-list-step-container">
|
<div className="mailpoet-clean-list-step-container">
|
||||||
<p>{MailPoet.I18n.t('cleanListText1')}</p>
|
<p>{MailPoet.I18n.t('cleanListText1')}</p>
|
||||||
@@ -26,3 +26,6 @@ export function CleanList({ onProceed }: Props): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CleanList.displayName = 'CleanList';
|
||||||
|
export { CleanList };
|
||||||
|
@@ -47,5 +47,5 @@ PreviousNextStepButtons.defaultProps = {
|
|||||||
onPreviousAction: () => {},
|
onPreviousAction: () => {},
|
||||||
onNextAction: () => {},
|
onNextAction: () => {},
|
||||||
};
|
};
|
||||||
|
PreviousNextStepButtons.displayName = 'PreviousNextStepButtons';
|
||||||
export { PreviousNextStepButtons };
|
export { PreviousNextStepButtons };
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps } from 'react-router-dom';
|
||||||
import { CleanList } from './clean_list';
|
import { CleanList } from './clean_list';
|
||||||
|
|
||||||
export function StepCleanList({ history }: RouteComponentProps): JSX.Element {
|
function StepCleanList({ history }: RouteComponentProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<CleanList onProceed={(): void => history.push('step_method_selection')} />
|
<CleanList onProceed={(): void => history.push('step_method_selection')} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StepCleanList.displayName = 'StepCleanList';
|
||||||
|
export { StepCleanList };
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import { useEffect, useState, useCallback } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { CleanList } from 'subscribers/importExport/import/clean_list';
|
import { CleanList } from 'subscribers/importExport/import/clean_list';
|
||||||
|
import { ErrorBoundary } from 'common';
|
||||||
import { InitialQuestion } from './step_input_validation/initial_question.jsx';
|
import { InitialQuestion } from './step_input_validation/initial_question.jsx';
|
||||||
import { WrongSourceBlock } from './step_input_validation/wrong_source_block.jsx';
|
import { WrongSourceBlock } from './step_input_validation/wrong_source_block.jsx';
|
||||||
import { LastSentQuestion } from './step_input_validation/last_sent_question.jsx';
|
import { LastSentQuestion } from './step_input_validation/last_sent_question.jsx';
|
||||||
@@ -30,17 +31,23 @@ function StepInputValidationComponent({ stepMethodSelectionData, history }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{importSource === undefined && (
|
{importSource === undefined && (
|
||||||
|
<ErrorBoundary>
|
||||||
<InitialQuestion onSubmit={setImportSource} history={history} />
|
<InitialQuestion onSubmit={setImportSource} history={history} />
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{importSource === 'address-book' && <WrongSourceBlock />}
|
{importSource === 'address-book' && <WrongSourceBlock />}
|
||||||
|
|
||||||
{importSource === 'existing-list' && lastSent === undefined && (
|
{importSource === 'existing-list' && lastSent === undefined && (
|
||||||
|
<ErrorBoundary>
|
||||||
<LastSentQuestion onSubmit={lastSentSubmit} />
|
<LastSentQuestion onSubmit={lastSentSubmit} />
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{importSource === 'existing-list' && lastSent === 'notRecently' && (
|
{importSource === 'existing-list' && lastSent === 'notRecently' && (
|
||||||
|
<ErrorBoundary>
|
||||||
<CleanList />
|
<CleanList />
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@@ -57,5 +57,5 @@ InitialQuestion.propTypes = {
|
|||||||
}).isRequired,
|
}).isRequired,
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
InitialQuestion.displayName = 'InitialQuestion';
|
||||||
export { InitialQuestion };
|
export { InitialQuestion };
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { MailPoet } from 'mailpoet';
|
import { MailPoet } from 'mailpoet';
|
||||||
import { Button } from 'common/button/button';
|
import { Button } from 'common/button/button';
|
||||||
@@ -63,5 +63,5 @@ function LastSentQuestion({ onSubmit }) {
|
|||||||
LastSentQuestion.propTypes = {
|
LastSentQuestion.propTypes = {
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
LastSentQuestion.displayName = 'LastSentQuestion';
|
||||||
export { LastSentQuestion };
|
export { LastSentQuestion };
|
||||||
|
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
|||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { MailPoet } from 'mailpoet';
|
import { MailPoet } from 'mailpoet';
|
||||||
|
import { ErrorBoundary } from 'common';
|
||||||
import { SelectImportMethod } from './step_method_selection/select_import_method.jsx';
|
import { SelectImportMethod } 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';
|
||||||
@@ -57,8 +58,11 @@ function StepMethodSelectionComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-settings-grid">
|
<div className="mailpoet-settings-grid">
|
||||||
|
<ErrorBoundary>
|
||||||
<SelectImportMethod activeMethod={method} onMethodChange={setMethod} />
|
<SelectImportMethod activeMethod={method} onMethodChange={setMethod} />
|
||||||
|
</ErrorBoundary>
|
||||||
{method === 'paste-method' && (
|
{method === 'paste-method' && (
|
||||||
|
<ErrorBoundary>
|
||||||
<MethodPaste
|
<MethodPaste
|
||||||
onPrevious={previousStep}
|
onPrevious={previousStep}
|
||||||
onValueChange={setPastedCsvData}
|
onValueChange={setPastedCsvData}
|
||||||
@@ -66,8 +70,10 @@ function StepMethodSelectionComponent({
|
|||||||
canFinish={!!pastedCsvData.trim()}
|
canFinish={!!pastedCsvData.trim()}
|
||||||
data={pastedCsvData}
|
data={pastedCsvData}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
{method === 'file-method' && (
|
{method === 'file-method' && (
|
||||||
|
<ErrorBoundary>
|
||||||
<MethodUpload
|
<MethodUpload
|
||||||
onPrevious={previousStep}
|
onPrevious={previousStep}
|
||||||
onValueChange={setFile}
|
onValueChange={setFile}
|
||||||
@@ -75,8 +81,10 @@ function StepMethodSelectionComponent({
|
|||||||
canFinish={!!file}
|
canFinish={!!file}
|
||||||
data={file}
|
data={file}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
{method === 'mailchimp-method' && (
|
{method === 'mailchimp-method' && (
|
||||||
|
<ErrorBoundary>
|
||||||
<MethodMailChimp
|
<MethodMailChimp
|
||||||
onPrevious={previousStep}
|
onPrevious={previousStep}
|
||||||
onFinish={(data) => {
|
onFinish={(data) => {
|
||||||
@@ -86,12 +94,15 @@ function StepMethodSelectionComponent({
|
|||||||
finish(data);
|
finish(data);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
{method === undefined && (
|
{method === undefined && (
|
||||||
|
<ErrorBoundary>
|
||||||
<PreviousNextStepButtons
|
<PreviousNextStepButtons
|
||||||
canGoNext={false}
|
canGoNext={false}
|
||||||
onPreviousAction={previousStep}
|
onPreviousAction={previousStep}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -104,5 +115,5 @@ StepMethodSelectionComponent.propTypes = {
|
|||||||
setStepMethodSelectionData: PropTypes.func.isRequired,
|
setStepMethodSelectionData: PropTypes.func.isRequired,
|
||||||
subscribersLimitForValidation: PropTypes.number.isRequired,
|
subscribersLimitForValidation: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
|
StepMethodSelectionComponent.diplayName = 'StepMethodSelection';
|
||||||
export const StepMethodSelection = withRouter(StepMethodSelectionComponent);
|
export const StepMethodSelection = withRouter(StepMethodSelectionComponent);
|
||||||
|
@@ -160,4 +160,6 @@ MethodMailChimp.defaultProps = {
|
|||||||
onPrevious: () => {},
|
onPrevious: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MethodMailChimp.displayName = 'MethodMailChimp';
|
||||||
|
|
||||||
export { MethodMailChimp };
|
export { MethodMailChimp };
|
||||||
|
@@ -70,5 +70,5 @@ MethodPaste.defaultProps = {
|
|||||||
onPrevious: () => {},
|
onPrevious: () => {},
|
||||||
data: '',
|
data: '',
|
||||||
};
|
};
|
||||||
|
MethodPaste.displayName = 'MethodPaste';
|
||||||
export { MethodPaste };
|
export { MethodPaste };
|
||||||
|
@@ -71,5 +71,5 @@ MethodUpload.defaultProps = {
|
|||||||
onFinish: () => {},
|
onFinish: () => {},
|
||||||
onPrevious: () => {},
|
onPrevious: () => {},
|
||||||
};
|
};
|
||||||
|
MethodUpload.displayName = 'MethodUpload';
|
||||||
export { MethodUpload };
|
export { MethodUpload };
|
||||||
|
@@ -78,5 +78,5 @@ SelectImportMethod.propTypes = {
|
|||||||
SelectImportMethod.defaultProps = {
|
SelectImportMethod.defaultProps = {
|
||||||
activeMethod: undefined,
|
activeMethod: undefined,
|
||||||
};
|
};
|
||||||
|
SelectImportMethod.displayName = 'SelectImportMethod';
|
||||||
export { SelectImportMethod };
|
export { SelectImportMethod };
|
||||||
|
@@ -6,6 +6,7 @@ import { withRouter } from 'react-router-dom';
|
|||||||
import ReactStringReplace from 'react-string-replace';
|
import ReactStringReplace from 'react-string-replace';
|
||||||
|
|
||||||
import { Button } from 'common/button/button';
|
import { Button } from 'common/button/button';
|
||||||
|
import { ErrorBoundary } from 'common';
|
||||||
|
|
||||||
function ResultMessage({ subscribersCount, segments, initialMessage }) {
|
function ResultMessage({ subscribersCount, segments, initialMessage }) {
|
||||||
if (subscribersCount) {
|
if (subscribersCount) {
|
||||||
@@ -33,6 +34,7 @@ ResultMessage.defaultProps = {
|
|||||||
subscribersCount: 0,
|
subscribersCount: 0,
|
||||||
initialMessage: '',
|
initialMessage: '',
|
||||||
};
|
};
|
||||||
|
ResultMessage.displayName = 'ResultMessage';
|
||||||
|
|
||||||
function NoAction({ createdSubscribers, updatedSubscribers }) {
|
function NoAction({ createdSubscribers, updatedSubscribers }) {
|
||||||
if (!createdSubscribers && !updatedSubscribers) {
|
if (!createdSubscribers && !updatedSubscribers) {
|
||||||
@@ -50,6 +52,7 @@ NoAction.defaultProps = {
|
|||||||
createdSubscribers: 0,
|
createdSubscribers: 0,
|
||||||
updatedSubscribers: 0,
|
updatedSubscribers: 0,
|
||||||
};
|
};
|
||||||
|
NoAction.displayName = 'NoAction';
|
||||||
|
|
||||||
function SuppressionListReminder({ createdSubscribers, updatedSubscribers }) {
|
function SuppressionListReminder({ createdSubscribers, updatedSubscribers }) {
|
||||||
if (createdSubscribers || updatedSubscribers) {
|
if (createdSubscribers || updatedSubscribers) {
|
||||||
@@ -91,6 +94,7 @@ SuppressionListReminder.defaultProps = {
|
|||||||
createdSubscribers: 0,
|
createdSubscribers: 0,
|
||||||
updatedSubscribers: 0,
|
updatedSubscribers: 0,
|
||||||
};
|
};
|
||||||
|
SuppressionListReminder.displayName = 'SuppressionListReminder';
|
||||||
|
|
||||||
function NoWelcomeEmail({ addedToSegmentWithWelcomeNotification }) {
|
function NoWelcomeEmail({ addedToSegmentWithWelcomeNotification }) {
|
||||||
if (addedToSegmentWithWelcomeNotification) {
|
if (addedToSegmentWithWelcomeNotification) {
|
||||||
@@ -106,6 +110,7 @@ NoWelcomeEmail.propTypes = {
|
|||||||
NoWelcomeEmail.defaultProps = {
|
NoWelcomeEmail.defaultProps = {
|
||||||
addedToSegmentWithWelcomeNotification: false,
|
addedToSegmentWithWelcomeNotification: false,
|
||||||
};
|
};
|
||||||
|
NoWelcomeEmail.diplayName = 'NoWelcomeEmail';
|
||||||
|
|
||||||
function StepResultsComponent({
|
function StepResultsComponent({
|
||||||
errors,
|
errors,
|
||||||
@@ -136,6 +141,7 @@ function StepResultsComponent({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ErrorBoundary>
|
||||||
<div className="updated">
|
<div className="updated">
|
||||||
<ResultMessage
|
<ResultMessage
|
||||||
subscribersCount={createdSubscribers}
|
subscribersCount={createdSubscribers}
|
||||||
@@ -157,10 +163,13 @@ function StepResultsComponent({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
|
<ErrorBoundary>
|
||||||
<SuppressionListReminder
|
<SuppressionListReminder
|
||||||
createdSubscribers={createdSubscribers}
|
createdSubscribers={createdSubscribers}
|
||||||
updatedSubscribers={updatedSubscribers}
|
updatedSubscribers={updatedSubscribers}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
<div className="mailpoet-settings-grid">
|
<div className="mailpoet-settings-grid">
|
||||||
<div className="mailpoet-settings-save">
|
<div className="mailpoet-settings-save">
|
||||||
<Button
|
<Button
|
||||||
@@ -203,5 +212,5 @@ StepResultsComponent.defaultProps = {
|
|||||||
updatedSubscribers: undefined,
|
updatedSubscribers: undefined,
|
||||||
addedToSegmentWithWelcomeNotification: undefined,
|
addedToSegmentWithWelcomeNotification: undefined,
|
||||||
};
|
};
|
||||||
|
StepResultsComponent.displayName = 'StepResultsComponent';
|
||||||
export const StepResults = withRouter(StepResultsComponent);
|
export const StepResults = withRouter(StepResultsComponent);
|
||||||
|
Reference in New Issue
Block a user