Add a component for stats
[MAILPOET-2791]
This commit is contained in:
@@ -0,0 +1,109 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import MailPoet from 'mailpoet';
|
||||||
|
import Hooks from 'wp-js-hooks';
|
||||||
|
import Grid from 'common/grid';
|
||||||
|
import StatsBadge from 'common/listings/newsletter_stats/stats';
|
||||||
|
|
||||||
|
import { NewsletterType } from './newsletter_type';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
newsletter: NewsletterType
|
||||||
|
}
|
||||||
|
|
||||||
|
const minNewslettersSent = 20;
|
||||||
|
const minNewslettersOpened = 5;
|
||||||
|
|
||||||
|
export const NewsletterGeneralStats = ({
|
||||||
|
newsletter,
|
||||||
|
}: Props) => {
|
||||||
|
const totalSent = newsletter.total_sent || 0;
|
||||||
|
|
||||||
|
let percentageClicked = 0;
|
||||||
|
let percentageOpened = 0;
|
||||||
|
let percentageUnsubscribed = 0;
|
||||||
|
if (totalSent > 0) {
|
||||||
|
percentageClicked = (newsletter.statistics.clicked * 100) / totalSent;
|
||||||
|
percentageOpened = (newsletter.statistics.opened * 100) / totalSent;
|
||||||
|
percentageUnsubscribed = (newsletter.statistics.unsubscribed * 100) / totalSent;
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
const headlineClicked = `${percentageClickedDisplay}% ${MailPoet.I18n.t('percentageClicked')}`;
|
||||||
|
|
||||||
|
const displayBadges = ((totalSent >= minNewslettersSent)
|
||||||
|
&& (newsletter.statistics.opened >= minNewslettersOpened)
|
||||||
|
);
|
||||||
|
|
||||||
|
const opened = (
|
||||||
|
<>
|
||||||
|
<div>{`${percentageOpenedDisplay}% ${MailPoet.I18n.t('percentageOpened')}`}</div>
|
||||||
|
{displayBadges && (
|
||||||
|
<StatsBadge
|
||||||
|
stat="opened"
|
||||||
|
rate={percentageOpened}
|
||||||
|
tooltipId={`opened-${newsletter.id || '0'}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const unsubscribed = (
|
||||||
|
<>
|
||||||
|
<div>{`${percentageUnsubscribedDisplay}% ${MailPoet.I18n.t('percentageUnsubscribed')}`}</div>
|
||||||
|
{displayBadges && (
|
||||||
|
<StatsBadge
|
||||||
|
stat="unsubscribed"
|
||||||
|
rate={percentageUnsubscribed}
|
||||||
|
tooltipId={`unsubscribed-${newsletter.id || '0'}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const clicked = (
|
||||||
|
<>
|
||||||
|
<div>{`${percentageClickedDisplay}% ${MailPoet.I18n.t('percentageClicked')}`}</div>
|
||||||
|
{displayBadges && (
|
||||||
|
<StatsBadge
|
||||||
|
stat="clicked"
|
||||||
|
rate={percentageClicked}
|
||||||
|
tooltipId={`clicked-${newsletter.id || '0'}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Grid.ThreeColumns>
|
||||||
|
<div>
|
||||||
|
{MailPoet.I18n.t('statsTotalSent')}
|
||||||
|
{': '}
|
||||||
|
{totalSent.toLocaleString()}
|
||||||
|
{opened}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{unsubscribed}
|
||||||
|
|
||||||
|
{clicked}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{Hooks.applyFilters('mailpoet_newsletters_revenues_stats', null, newsletter.statistics.revenue)}
|
||||||
|
</div>
|
||||||
|
</Grid.ThreeColumns>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href="https://kb.mailpoet.com/article/190-whats-a-good-email-open-rate"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
data-beacon-article="58f671152c7d3a057f8858e8"
|
||||||
|
>
|
||||||
|
{MailPoet.I18n.t('readMoreOnStats')}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
17
assets/js/src/newsletters/campaign_stats/newsletter_type.ts
Normal file
17
assets/js/src/newsletters/campaign_stats/newsletter_type.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export type NewsletterType = {
|
||||||
|
id: string
|
||||||
|
total_sent: number
|
||||||
|
subject: string
|
||||||
|
queue: object
|
||||||
|
clicked_links: {cnt: string, url: string}[]
|
||||||
|
statistics: {
|
||||||
|
clicked: number
|
||||||
|
opened: number
|
||||||
|
unsubscribed: number
|
||||||
|
revenue: {
|
||||||
|
value: number
|
||||||
|
formatted: string
|
||||||
|
count: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +1,12 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import Hooks from 'wp-js-hooks';
|
import Hooks from 'wp-js-hooks';
|
||||||
import MailPoet from 'mailpoet';
|
import MailPoet from 'mailpoet';
|
||||||
import { Link, withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import InvalidMssKeyNotice from 'notices/invalid_mss_key_notice';
|
import InvalidMssKeyNotice from 'notices/invalid_mss_key_notice';
|
||||||
import { TopBarWithBeamer } from 'common/top_bar/top_bar';
|
import { TopBarWithBeamer } from 'common/top_bar/top_bar';
|
||||||
|
|
||||||
import NewsletterGeneralStats from './newsletter_stats.jsx';
|
import { NewsletterGeneralStats } from './newsletter_general_stats';
|
||||||
|
import { NewsletterType } from './newsletter_type';
|
||||||
import NewsletterStatsInfo from './newsletter_info.jsx';
|
import NewsletterStatsInfo from './newsletter_info.jsx';
|
||||||
import PremiumBanner from './premium_banner.jsx';
|
import PremiumBanner from './premium_banner.jsx';
|
||||||
import Heading from '../../common/typography/heading/heading';
|
import Heading from '../../common/typography/heading/heading';
|
||||||
@@ -37,12 +38,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
item?: {
|
item?: NewsletterType
|
||||||
id: string,
|
|
||||||
queue: object
|
|
||||||
subject: string
|
|
||||||
clicked_links: object[]
|
|
||||||
}
|
|
||||||
loading: boolean
|
loading: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +89,7 @@ const CampaignStatsPage = ({ match, history, location }: Props) => {
|
|||||||
return () => {
|
return () => {
|
||||||
showWPScreenOptions();
|
showWPScreenOptions();
|
||||||
};
|
};
|
||||||
}, [match.params.id, loadItem]);
|
}, [match.params.id, loadItem, state.item]);
|
||||||
|
|
||||||
const { item, loading } = state;
|
const { item, loading } = state;
|
||||||
const newsletter = item;
|
const newsletter = item;
|
||||||
@@ -122,14 +118,10 @@ const CampaignStatsPage = ({ match, history, location }: Props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mailpoet_stat_triple-spaced">
|
<div className="mailpoet_stat_triple-spaced">
|
||||||
<div className="mailpoet_stat_info">
|
|
||||||
<NewsletterStatsInfo newsletter={newsletter} />
|
<NewsletterStatsInfo newsletter={newsletter} />
|
||||||
</div>
|
|
||||||
<div className="mailpoet_stat_general">
|
|
||||||
<NewsletterGeneralStats newsletter={newsletter} />
|
<NewsletterGeneralStats newsletter={newsletter} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ clear: 'both' }} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>{MailPoet.I18n.t('clickedLinks')}</h2>
|
<h2>{MailPoet.I18n.t('clickedLinks')}</h2>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user