Add detailed stats page support in Free [PREMIUM-1]
This commit is contained in:
@ -209,8 +209,8 @@ const ListingItems = React.createClass({
|
||||
className="colspanchange">
|
||||
{
|
||||
(this.props.loading === true)
|
||||
? MailPoet.I18n.t('loadingItems')
|
||||
: MailPoet.I18n.t('noItemsFound')
|
||||
? (this.props.messages.onLoadingItems || MailPoet.I18n.t('loadingItems'))
|
||||
: (this.props.messages.onNoItemsFound || MailPoet.I18n.t('noItemsFound'))
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
@ -793,6 +793,12 @@ const Listing = React.createClass({
|
||||
groups = false;
|
||||
}
|
||||
|
||||
// messages
|
||||
let messages = {};
|
||||
if (this.props.messages !== undefined) {
|
||||
messages = this.props.messages;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ groups }
|
||||
@ -846,6 +852,7 @@ const Listing = React.createClass({
|
||||
count={ this.state.count }
|
||||
limit={ this.state.limit }
|
||||
item_actions={ item_actions }
|
||||
messages={ messages }
|
||||
items={ items } />
|
||||
|
||||
<tfoot>
|
||||
|
@ -1,9 +1,11 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ReactStringReplace from 'react-string-replace'
|
||||
import { Link } from 'react-router'
|
||||
import MailPoet from 'mailpoet'
|
||||
import classNames from 'classnames'
|
||||
import jQuery from 'jquery'
|
||||
import Hooks from 'wp-js-hooks'
|
||||
|
||||
const _QueueMixin = {
|
||||
pauseSending: function(newsletter) {
|
||||
@ -146,6 +148,9 @@ const _StatisticsMixin = {
|
||||
&& newsletter.queue
|
||||
&& newsletter.queue.status !== 'scheduled'
|
||||
) {
|
||||
let params = {};
|
||||
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
|
||||
|
||||
const total_sent = ~~(newsletter.queue.count_processed);
|
||||
|
||||
let percentage_clicked = 0;
|
||||
@ -153,22 +158,32 @@ const _StatisticsMixin = {
|
||||
let percentage_unsubscribed = 0;
|
||||
|
||||
if (total_sent > 0) {
|
||||
percentage_clicked = Math.round(
|
||||
(~~(newsletter.statistics.clicked) * 100) / total_sent
|
||||
);
|
||||
percentage_opened = Math.round(
|
||||
(~~(newsletter.statistics.opened) * 100) / total_sent
|
||||
);
|
||||
percentage_unsubscribed = Math.round(
|
||||
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
|
||||
);
|
||||
percentage_clicked = (newsletter.statistics.clicked * 100) / total_sent;
|
||||
percentage_opened = (newsletter.statistics.opened * 100) / total_sent;
|
||||
percentage_unsubscribed = (newsletter.statistics.unsubscribed * 100) / total_sent;
|
||||
}
|
||||
|
||||
return (
|
||||
// format to 1 decimal place
|
||||
percentage_clicked = percentage_clicked.toFixed(1);
|
||||
percentage_opened = percentage_opened.toFixed(1);
|
||||
percentage_unsubscribed = percentage_unsubscribed.toFixed(1);
|
||||
|
||||
const content = (
|
||||
<span>
|
||||
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
|
||||
</span>
|
||||
);
|
||||
|
||||
if (total_sent > 0 && params.link) {
|
||||
return (
|
||||
<Link
|
||||
key={ `stats-${newsletter.id}` }
|
||||
to={ params.link }
|
||||
>{ content }</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
} else {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
|
@ -11,6 +11,7 @@ import classNames from 'classnames'
|
||||
import jQuery from 'jquery'
|
||||
import MailPoet from 'mailpoet'
|
||||
import _ from 'underscore'
|
||||
import Hooks from 'wp-js-hooks'
|
||||
|
||||
const mailpoet_roles = window.mailpoet_roles || {};
|
||||
const mailpoet_segments = window.mailpoet_segments || {};
|
||||
@ -281,24 +282,37 @@ const NewsletterListWelcome = React.createClass({
|
||||
return;
|
||||
}
|
||||
|
||||
let params = {};
|
||||
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
|
||||
|
||||
if (newsletter.total_sent > 0 && newsletter.statistics) {
|
||||
const total_sent = ~~(newsletter.total_sent);
|
||||
|
||||
const percentage_clicked = Math.round(
|
||||
(~~(newsletter.statistics.clicked) * 100) / total_sent
|
||||
);
|
||||
const percentage_opened = Math.round(
|
||||
(~~(newsletter.statistics.opened) * 100) / total_sent
|
||||
);
|
||||
const percentage_unsubscribed = Math.round(
|
||||
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
|
||||
);
|
||||
let percentage_clicked = (newsletter.statistics.clicked * 100) / total_sent;
|
||||
let percentage_opened = (newsletter.statistics.opened * 100) / total_sent;
|
||||
let percentage_unsubscribed = (newsletter.statistics.unsubscribed * 100) / total_sent;
|
||||
|
||||
return (
|
||||
// format to 1 decimal place
|
||||
percentage_clicked = percentage_clicked.toFixed(1);
|
||||
percentage_opened = percentage_opened.toFixed(1);
|
||||
percentage_unsubscribed = percentage_unsubscribed.toFixed(1);
|
||||
|
||||
const content = (
|
||||
<span>
|
||||
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
|
||||
</span>
|
||||
);
|
||||
|
||||
if (params.link) {
|
||||
return (
|
||||
<Link
|
||||
key={ `stats-${newsletter.id}` }
|
||||
to={ params.link }
|
||||
>{ content }</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
} else {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
|
@ -2,6 +2,7 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Router, Route, IndexRedirect, Link, useRouterHistory } from 'react-router'
|
||||
import { createHashHistory } from 'history'
|
||||
import Hooks from 'wp-js-hooks'
|
||||
|
||||
import NewsletterTypes from 'newsletters/types.jsx'
|
||||
import NewsletterTemplates from 'newsletters/templates.jsx'
|
||||
@ -27,6 +28,9 @@ const App = React.createClass({
|
||||
const container = document.getElementById('newsletters_container');
|
||||
|
||||
if(container) {
|
||||
let extra_routes = [];
|
||||
extra_routes = Hooks.applyFilters('mailpoet_newsletters_before_router', extra_routes);
|
||||
|
||||
const mailpoet_listing = ReactDOM.render((
|
||||
<Router history={ history }>
|
||||
<Route path="/" component={ App }>
|
||||
@ -46,6 +50,8 @@ if(container) {
|
||||
<Route name="template" path="template/:id" component={ NewsletterTemplates } />
|
||||
{/* Sending options */}
|
||||
<Route path="send/:id" component={ NewsletterSend } />
|
||||
{/* Extra routes */}
|
||||
{ extra_routes.map(rt => <Route key={rt.path} path={rt.path} component={rt.component} />) }
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
@ -300,7 +300,8 @@ class Migrator {
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'sent_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)',
|
||||
'PRIMARY KEY (id),',
|
||||
'KEY newsletter_id (newsletter_id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
@ -316,6 +317,7 @@ class Migrator {
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'KEY newsletter_id (newsletter_id),',
|
||||
'KEY queue_id (queue_id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -329,6 +331,7 @@ class Migrator {
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'KEY newsletter_id (newsletter_id),',
|
||||
'KEY queue_id (queue_id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -342,6 +345,7 @@ class Migrator {
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'KEY newsletter_id (newsletter_id),',
|
||||
'KEY queue_id (queue_id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
|
@ -264,7 +264,8 @@ class Newsletter extends Model {
|
||||
'status',
|
||||
'count_processed',
|
||||
'count_total',
|
||||
'scheduled_at'
|
||||
'scheduled_at',
|
||||
'created_at'
|
||||
));
|
||||
if($queue === false) {
|
||||
$this->queue = false;
|
||||
|
@ -3,7 +3,6 @@
|
||||
use Codeception\Util\Stub;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Models\StatisticsClicks;
|
||||
use MailPoet\Models\StatisticsOpens;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Statistics\Track\Opens;
|
||||
@ -90,6 +89,5 @@ class OpensTest extends MailPoetTest {
|
||||
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . StatisticsOpens::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . StatisticsClicks::$_table);
|
||||
}
|
||||
}
|
@ -65,7 +65,11 @@ baseConfig = {
|
||||
include: require.resolve('react'),
|
||||
loader: 'expose-loader?' + globalPrefix + '.React',
|
||||
},
|
||||
{
|
||||
{
|
||||
include: require.resolve('react-router'),
|
||||
loader: 'expose-loader?' + globalPrefix + '.ReactRouter',
|
||||
},
|
||||
{
|
||||
include: require.resolve('react-string-replace'),
|
||||
loader: 'expose-loader?' + globalPrefix + '.ReactStringReplace',
|
||||
},
|
||||
@ -73,6 +77,10 @@ baseConfig = {
|
||||
test: /wp-js-hooks/i,
|
||||
loader: 'expose-loader?' + globalPrefix + '.Hooks!exports-loader?wp.hooks',
|
||||
},
|
||||
{
|
||||
test: /listing.jsx/i,
|
||||
loader: 'expose-loader?' + globalPrefix + '.Listing!babel-loader',
|
||||
},
|
||||
{
|
||||
include: /Blob.js$/,
|
||||
loader: 'exports-loader?window.Blob',
|
||||
@ -124,7 +132,8 @@ config.push(_.extend({}, baseConfig, {
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-router',
|
||||
'react-string-replace'
|
||||
'react-string-replace',
|
||||
'listing/listing.jsx'
|
||||
],
|
||||
admin: [
|
||||
'subscribers/subscribers.jsx',
|
||||
|
Reference in New Issue
Block a user