diff --git a/assets/js/src/newsletters/listings/mixins.jsx b/assets/js/src/newsletters/listings/mixins.jsx index 8930f3f92a..9e7bd7bb64 100644 --- a/assets/js/src/newsletters/listings/mixins.jsx +++ b/assets/js/src/newsletters/listings/mixins.jsx @@ -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 ( - - {content} - - ); - } - return ( - - {content} - - ); -} - -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 ( - {MailPoet.I18n.t('notSentYet')} - ); - } - - 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 = ( -
-
- - { percentageOpenedDisplay } - % - {' '} - - -
-
- - { percentageClickedDisplay } - % - {' '} - - -
-
- - { percentageUnsubscribedDisplay } - % - -
-
- ); - } else { - // display simple stats - openedAndClickedStats = ( -
- - { percentageOpenedDisplay } - %, - { ' ' } - { percentageClickedDisplay } - % - - , - {' '} - { percentageUnsubscribedDisplay } - % - - -
- ); - } - - const wrapContentInLink = (content, idPrefix) => wrapInLink( - content, - params, - `${idPrefix}-${newsletter.id}`, - totalSent - ); - - const content = ( - <> - { wrapContentInLink(openedAndClickedStats, 'opened-and-clicked') } - { revenue !== null && revenue.value > 0 && ( -
- { wrapContentInLink(revenue.formatted, 'revenue') } - {' '} - -
- ) } - { tooEarlyForStats && wrapContentInLink( - ( -
- {MailPoet.I18n.t('checkBackInHours').replace('%$1d', showStatsTimeout - sentHoursAgo)} -
- ), - '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 = ( -
- - {MailPoet.I18n.t('improveThisLinkText')} - -
- ); - } - - return ( -
- {content} - {afterContent} -
- ); }; export const addStatsCTAAction = (actions) => { diff --git a/assets/js/src/newsletters/listings/notification_history.jsx b/assets/js/src/newsletters/listings/notification_history.jsx index c1c1982ce9..ccc53f623c 100644 --- a/assets/js/src/newsletters/listings/notification_history.jsx +++ b/assets/js/src/newsletters/listings/notification_history.jsx @@ -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 { (mailpoetTrackingEnabled === true) ? ( - { renderStatistics(newsletter, undefined, meta.current_time) } + ) : null } diff --git a/assets/js/src/newsletters/listings/standard.jsx b/assets/js/src/newsletters/listings/standard.jsx index f1f14a3c21..e73a55abbf 100644 --- a/assets/js/src/newsletters/listings/standard.jsx +++ b/assets/js/src/newsletters/listings/standard.jsx @@ -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 { (mailpoetTrackingEnabled === true) ? ( - { renderStatistics(newsletter, undefined, meta.current_time) } + ) : null } diff --git a/assets/js/src/newsletters/listings/statistics.jsx b/assets/js/src/newsletters/listings/statistics.jsx new file mode 100644 index 0000000000..55af075982 --- /dev/null +++ b/assets/js/src/newsletters/listings/statistics.jsx @@ -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 ( + + {content} + + ); + } + return ( + + {content} + + ); +}; + +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 ( + {MailPoet.I18n.t('notSentYet')} + ); + } + + 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 = ( +
+
+ + {percentageOpenedDisplay} + % + {' '} + + +
+
+ + {percentageClickedDisplay} + % + {' '} + + +
+
+ + {percentageUnsubscribedDisplay} + % + +
+
+ ); + } else { + // display simple stats + openedAndClickedStats = ( +
+ + {percentageOpenedDisplay} + %, + {' '} + {percentageClickedDisplay} + % + + , + {' '} + {percentageUnsubscribedDisplay} + % + + +
+ ); + } + + const wrapContentInLink = (content, idPrefix) => wrapInLink( + content, + params, + `${idPrefix}-${newsletter.id}`, + totalSent + ); + + const content = ( + <> + {wrapContentInLink(openedAndClickedStats, 'opened-and-clicked')} + {revenue !== null && revenue.value > 0 && ( +
+ {wrapContentInLink(revenue.formatted, 'revenue')} + {' '} + +
+ )} + {tooEarlyForStats && wrapContentInLink( + ( +
+ {MailPoet.I18n.t('checkBackInHours').replace('%$1d', showStatsTimeout - sentHoursAgo)} +
+ ), + '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 = ( +
+ + {MailPoet.I18n.t('improveThisLinkText')} + +
+ ); + } + + return ( +
+ {content} + {afterContent} +
+ ); +}; +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; diff --git a/assets/js/src/newsletters/listings/welcome.jsx b/assets/js/src/newsletters/listings/welcome.jsx index 5aea970eac..f3274eb95e 100644 --- a/assets/js/src/newsletters/listings/welcome.jsx +++ b/assets/js/src/newsletters/listings/welcome.jsx @@ -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 { (mailpoetTrackingEnabled === true) ? ( - { renderStatistics( - newsletter, - newsletter.total_sent > 0 && newsletter.statistics - ) } + 0 && newsletter.statistics} + /> ) : null }