Rewrite renderStatistics as a React component

This commit is contained in:
Amine Ben hammou
2019-06-21 15:17:48 +01:00
committed by M. Shull
parent d9857731ea
commit b1d521f929
5 changed files with 274 additions and 243 deletions

View File

@ -1,247 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import ReactStringReplace from 'react-string-replace';
import { Link } from 'react-router-dom';
import MailPoet from 'mailpoet';
import moment from 'moment';
import jQuery from 'jquery';
import Hooks from 'wp-js-hooks';
import StatsBadge from 'newsletters/badges/stats.jsx';
import HelpTooltip from 'help-tooltip.jsx';
function trackStatsCTAClicked() {
export const trackStatsCTAClicked = () => {
MailPoet.trackEvent(
'User has clicked a CTA to view detailed stats',
{ 'MailPoet Free version': window.mailpoet_version }
);
}
function wrapInLink(content, params, id, totalSent) {
if (totalSent <= 0 || !params.link) {
return content;
}
if (params.externalLink) {
return (
<a
key={`stats-${id}`}
href={params.link}
onClick={params.onClick || null}
>
{content}
</a>
);
}
return (
<Link
key={`stats-${id}`}
to={params.link}
onClick={params.onClick || null}
>
{content}
</Link>
);
}
const addStatsCTALink = (params) => {
if (window.mailpoet_premium_active) {
return params;
}
const newParams = params;
newParams.link = 'admin.php?page=mailpoet-premium';
newParams.externalLink = true;
newParams.onClick = trackStatsCTAClicked;
return newParams;
};
export const renderStatistics = (newsletter, isSent, currentTime) => {
let sent = isSent;
if (sent === undefined) {
// condition for standard and post notification listings
sent = newsletter.statistics
&& newsletter.queue
&& newsletter.queue.status !== 'scheduled';
}
if (!sent) {
return (
<span>{MailPoet.I18n.t('notSentYet')}</span>
);
}
let params = {};
Hooks.addFilter('mailpoet_newsletters_listing_stats_before', 'mailpoet', addStatsCTALink);
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
// welcome emails provide explicit total_sent value
const totalSent = Number((newsletter.total_sent || newsletter.queue.count_processed));
let percentageClicked = 0;
let percentageOpened = 0;
let percentageUnsubscribed = 0;
let revenue = null;
if (totalSent > 0) {
percentageClicked = (newsletter.statistics.clicked * 100) / totalSent;
percentageOpened = (newsletter.statistics.opened * 100) / totalSent;
percentageUnsubscribed = (newsletter.statistics.unsubscribed * 100) / totalSent;
revenue = newsletter.statistics.revenue;
}
// format to 1 decimal place
const percentageClickedDisplay = MailPoet.Num.toLocaleFixed(percentageClicked, 1);
const percentageOpenedDisplay = MailPoet.Num.toLocaleFixed(percentageOpened, 1);
const percentageUnsubscribedDisplay = MailPoet.Num.toLocaleFixed(percentageUnsubscribed, 1);
let showStatsTimeout;
let newsletterDate;
let sentHoursAgo;
let tooEarlyForStats;
let showKbLink;
if (currentTime !== undefined) {
// standard emails and post notifications:
// display green box for newsletters that were just sent
showStatsTimeout = 6; // in hours
newsletterDate = newsletter.queue.scheduled_at || newsletter.queue.created_at;
sentHoursAgo = moment(currentTime).diff(moment(newsletterDate), 'hours');
tooEarlyForStats = sentHoursAgo < showStatsTimeout;
showKbLink = true;
} else {
// welcome emails: no green box and KB link
tooEarlyForStats = false;
showKbLink = false;
}
const improveStatsKBLink = 'http://beta.docs.mailpoet.com/article/191-how-to-improve-my-open-and-click-rates';
// thresholds to display badges
const minNewslettersSent = 20;
const minNewsletterOpens = 5;
let openedAndClickedStats;
if (totalSent >= minNewslettersSent
&& newsletter.statistics.opened >= minNewsletterOpens
&& !tooEarlyForStats
) {
// display stats with badges
openedAndClickedStats = (
<div className="mailpoet_stats_text">
<div>
<span>
{ percentageOpenedDisplay }
%
{' '}
</span>
<StatsBadge
stat="opened"
rate={percentageOpened}
tooltipId={`opened-${newsletter.id}`}
/>
</div>
<div>
<span>
{ percentageClickedDisplay }
%
{' '}
</span>
<StatsBadge
stat="clicked"
rate={percentageClicked}
tooltipId={`clicked-${newsletter.id}`}
/>
</div>
<div>
<span className="mailpoet_stat_hidden">
{ percentageUnsubscribedDisplay }
%
</span>
</div>
</div>
);
} else {
// display simple stats
openedAndClickedStats = (
<div>
<span className="mailpoet_stats_text">
{ percentageOpenedDisplay }
%,
{ ' ' }
{ percentageClickedDisplay }
%
<span className="mailpoet_stat_hidden">
,
{' '}
{ percentageUnsubscribedDisplay }
%
</span>
</span>
</div>
);
}
const wrapContentInLink = (content, idPrefix) => wrapInLink(
content,
params,
`${idPrefix}-${newsletter.id}`,
totalSent
);
const content = (
<>
{ wrapContentInLink(openedAndClickedStats, 'opened-and-clicked') }
{ revenue !== null && revenue.value > 0 && (
<div className="mailpoet_stats_text">
{ wrapContentInLink(revenue.formatted, 'revenue') }
{' '}
<HelpTooltip
tooltip={MailPoet.I18n.t('revenueStatsTooltip')}
place="left"
tooltipId="helpTooltipStatsRevenue"
/>
</div>
) }
{ tooEarlyForStats && wrapContentInLink(
(
<div className="mailpoet_badge mailpoet_badge_green">
{MailPoet.I18n.t('checkBackInHours').replace('%$1d', showStatsTimeout - sentHoursAgo)}
</div>
),
'check-back'
) }
</>
);
// thresholds to display bad open rate help
const maxPercentageOpened = 5;
const minSentHoursAgo = 24;
const minTotalSent = 10;
let afterContent;
if (showKbLink
&& percentageOpened < maxPercentageOpened
&& sentHoursAgo >= minSentHoursAgo
&& totalSent >= minTotalSent
) {
// help link for bad open rate
afterContent = (
<div>
<a
href={improveStatsKBLink}
target="_blank"
rel="noopener noreferrer"
className="mailpoet_stat_link_small"
>
{MailPoet.I18n.t('improveThisLinkText')}
</a>
</div>
);
}
return (
<div>
{content}
{afterContent}
</div>
);
};
export const addStatsCTAAction = (actions) => {

View File

@ -12,8 +12,8 @@ import ListingHeading from 'newsletters/listings/heading.jsx';
import FeatureAnnouncement from 'announcements/feature_announcement.jsx';
import QueueStatus from 'newsletters/listings/queue_status.jsx';
import Statistics from 'newsletters/listings/statistics.jsx';
import {
renderStatistics,
addStatsCTAAction,
checkCronStatus,
checkMailerStatus,
@ -102,7 +102,7 @@ const NewsletterListNotificationHistory = createReactClass({ // eslint-disable-l
</td>
{ (mailpoetTrackingEnabled === true) ? (
<td className="column" data-colname={MailPoet.I18n.t('statistics')}>
{ renderStatistics(newsletter, undefined, meta.current_time) }
<Statistics newsletter={newsletter} currentTime={meta.current_time} />
</td>
) : null }
<td className="column-date" data-colname={MailPoet.I18n.t('sentOn')}>

View File

@ -11,8 +11,8 @@ import ListingTabs from 'newsletters/listings/tabs.jsx';
import ListingHeading from 'newsletters/listings/heading.jsx';
import FeatureAnnouncement from 'announcements/feature_announcement.jsx';
import QueueStatus from 'newsletters/listings/queue_status.jsx';
import Statistics from 'newsletters/listings/statistics.jsx';
import {
renderStatistics,
addStatsCTAAction,
checkCronStatus,
checkMailerStatus,
@ -213,7 +213,7 @@ const NewsletterListStandard = createReactClass({ // eslint-disable-line react/p
</td>
{ (mailpoetTrackingEnabled === true) ? (
<td className="column" data-colname={MailPoet.I18n.t('statistics')}>
{ renderStatistics(newsletter, undefined, meta.current_time) }
<Statistics newsletter={newsletter} currentTime={meta.current_time} />
</td>
) : null }
<td className="column-date" data-colname={MailPoet.I18n.t('sentOn')}>

View File

@ -0,0 +1,264 @@
import React from 'react';
import moment from 'moment';
import MailPoet from 'mailpoet';
import Hooks from 'wp-js-hooks';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import HelpTooltip from 'help-tooltip.jsx';
import StatsBadge from 'newsletters/badges/stats.jsx';
import { trackStatsCTAClicked } from 'newsletters/listings/mixins.jsx';
const wrapInLink = (content, params, id, totalSent) => {
if (totalSent <= 0 || !params.link) {
return content;
}
if (params.externalLink) {
return (
<a
key={`stats-${id}`}
href={params.link}
onClick={params.onClick || null}
>
{content}
</a>
);
}
return (
<Link
key={`stats-${id}`}
to={params.link}
onClick={params.onClick || null}
>
{content}
</Link>
);
};
const addStatsCTALink = (params) => {
if (window.mailpoet_premium_active) {
return params;
}
const newParams = params;
newParams.link = 'admin.php?page=mailpoet-premium';
newParams.externalLink = true;
newParams.onClick = trackStatsCTAClicked;
return newParams;
};
const Statistics = ({ newsletter, isSent, currentTime }) => {
let sent = isSent;
if (sent === undefined) {
// condition for standard and post notification listings
sent = newsletter.statistics
&& newsletter.queue
&& newsletter.queue.status !== 'scheduled';
}
if (!sent) {
return (
<span>{MailPoet.I18n.t('notSentYet')}</span>
);
}
let params = {};
Hooks.addFilter('mailpoet_newsletters_listing_stats_before', 'mailpoet', addStatsCTALink);
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
// welcome emails provide explicit total_sent value
const totalSent = Number((newsletter.total_sent || newsletter.queue.count_processed));
let percentageClicked = 0;
let percentageOpened = 0;
let percentageUnsubscribed = 0;
let revenue = null;
if (totalSent > 0) {
percentageClicked = (newsletter.statistics.clicked * 100) / totalSent;
percentageOpened = (newsletter.statistics.opened * 100) / totalSent;
percentageUnsubscribed = (newsletter.statistics.unsubscribed * 100) / totalSent;
revenue = newsletter.statistics.revenue;
}
// format to 1 decimal place
const percentageClickedDisplay = MailPoet.Num.toLocaleFixed(percentageClicked, 1);
const percentageOpenedDisplay = MailPoet.Num.toLocaleFixed(percentageOpened, 1);
const percentageUnsubscribedDisplay = MailPoet.Num.toLocaleFixed(percentageUnsubscribed, 1);
let showStatsTimeout;
let newsletterDate;
let sentHoursAgo;
let tooEarlyForStats;
let showKbLink;
if (currentTime !== undefined) {
// standard emails and post notifications:
// display green box for newsletters that were just sent
showStatsTimeout = 6; // in hours
newsletterDate = newsletter.queue.scheduled_at || newsletter.queue.created_at;
sentHoursAgo = moment(currentTime).diff(moment(newsletterDate), 'hours');
tooEarlyForStats = sentHoursAgo < showStatsTimeout;
showKbLink = true;
} else {
// welcome emails: no green box and KB link
tooEarlyForStats = false;
showKbLink = false;
}
const improveStatsKBLink = 'http://beta.docs.mailpoet.com/article/191-how-to-improve-my-open-and-click-rates';
// thresholds to display badges
const minNewslettersSent = 20;
const minNewsletterOpens = 5;
let openedAndClickedStats;
if (totalSent >= minNewslettersSent
&& newsletter.statistics.opened >= minNewsletterOpens
&& !tooEarlyForStats
) {
// display stats with badges
openedAndClickedStats = (
<div className="mailpoet_stats_text">
<div>
<span>
{percentageOpenedDisplay}
%
{' '}
</span>
<StatsBadge
stat="opened"
rate={percentageOpened}
tooltipId={`opened-${newsletter.id}`}
/>
</div>
<div>
<span>
{percentageClickedDisplay}
%
{' '}
</span>
<StatsBadge
stat="clicked"
rate={percentageClicked}
tooltipId={`clicked-${newsletter.id}`}
/>
</div>
<div>
<span className="mailpoet_stat_hidden">
{percentageUnsubscribedDisplay}
%
</span>
</div>
</div>
);
} else {
// display simple stats
openedAndClickedStats = (
<div>
<span className="mailpoet_stats_text">
{percentageOpenedDisplay}
%,
{' '}
{percentageClickedDisplay}
%
<span className="mailpoet_stat_hidden">
,
{' '}
{percentageUnsubscribedDisplay}
%
</span>
</span>
</div>
);
}
const wrapContentInLink = (content, idPrefix) => wrapInLink(
content,
params,
`${idPrefix}-${newsletter.id}`,
totalSent
);
const content = (
<>
{wrapContentInLink(openedAndClickedStats, 'opened-and-clicked')}
{revenue !== null && revenue.value > 0 && (
<div className="mailpoet_stats_text">
{wrapContentInLink(revenue.formatted, 'revenue')}
{' '}
<HelpTooltip
tooltip={MailPoet.I18n.t('revenueStatsTooltip')}
place="left"
tooltipId="helpTooltipStatsRevenue"
/>
</div>
)}
{tooEarlyForStats && wrapContentInLink(
(
<div className="mailpoet_badge mailpoet_badge_green">
{MailPoet.I18n.t('checkBackInHours').replace('%$1d', showStatsTimeout - sentHoursAgo)}
</div>
),
'check-back'
)}
</>
);
// thresholds to display bad open rate help
const maxPercentageOpened = 5;
const minSentHoursAgo = 24;
const minTotalSent = 10;
let afterContent;
if (showKbLink
&& percentageOpened < maxPercentageOpened
&& sentHoursAgo >= minSentHoursAgo
&& totalSent >= minTotalSent
) {
// help link for bad open rate
afterContent = (
<div>
<a
href={improveStatsKBLink}
target="_blank"
rel="noopener noreferrer"
className="mailpoet_stat_link_small"
>
{MailPoet.I18n.t('improveThisLinkText')}
</a>
</div>
);
}
return (
<div>
{content}
{afterContent}
</div>
);
};
Statistics.propTypes = {
newsletter: PropTypes.shape({
id: PropTypes.number.isRequired,
queue: PropTypes.shape({
status: PropTypes.string,
count_processed: PropTypes.number.isRequired,
count_total: PropTypes.number.isRequired,
created_at: PropTypes.instanceOf(Date),
scheduled_at: PropTypes.instanceOf(Date),
}),
total_sent: PropTypes.number,
statistics: PropTypes.shape({
clicked: PropTypes.number,
opened: PropTypes.number,
unsubscribed: PropTypes.number,
revenue: PropTypes.number,
}),
}).isRequired,
isSent: PropTypes.bool,
currentTime: PropTypes.instanceOf(Date),
};
Statistics.defaultProps = {
isSent: undefined,
currentTime: undefined,
};
export default Statistics;

View File

@ -8,8 +8,8 @@ import ListingTabs from 'newsletters/listings/tabs.jsx';
import ListingHeading from 'newsletters/listings/heading.jsx';
import FeatureAnnouncement from 'announcements/feature_announcement.jsx';
import Statistics from 'newsletters/listings/statistics.jsx';
import {
renderStatistics,
addStatsCTAAction,
checkCronStatus,
checkMailerStatus,
@ -332,10 +332,10 @@ const NewsletterListWelcome = createReactClass({ // eslint-disable-line react/pr
</td>
{ (mailpoetTrackingEnabled === true) ? (
<td className="column" data-colname={MailPoet.I18n.t('statistics')}>
{ renderStatistics(
newsletter,
newsletter.total_sent > 0 && newsletter.statistics
) }
<Statistics
newsletter={newsletter}
isSent={newsletter.total_sent > 0 && newsletter.statistics}
/>
</td>
) : null }
<td className="column-date" data-colname={MailPoet.I18n.t('lastModifiedOn')}>