mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
Added newsletter open rate stats
refs https://github.com/TryGhost/Team/issues/469 - Adds new chart to track newsletter open rates on dashboard
This commit is contained in:
parent
6592fabbe9
commit
e554e26b02
3 changed files with 72 additions and 1 deletions
|
@ -38,6 +38,13 @@ export default class DashboardController extends Controller {
|
||||||
@tracked
|
@tracked
|
||||||
topMembersLoading = false;
|
topMembersLoading = false;
|
||||||
|
|
||||||
|
@tracked
|
||||||
|
newsletterOpenRatesData = null;
|
||||||
|
@tracked
|
||||||
|
newsletterOpenRatesError = null;
|
||||||
|
@tracked
|
||||||
|
newsletterOpenRatesLoading = false;
|
||||||
|
|
||||||
get showTopMembers() {
|
get showTopMembers() {
|
||||||
return this.feature.get('emailAnalytics') && this.settings.get('emailTrackOpens');
|
return this.feature.get('emailAnalytics') && this.settings.get('emailTrackOpens');
|
||||||
}
|
}
|
||||||
|
@ -130,6 +137,7 @@ export default class DashboardController extends Controller {
|
||||||
loadCharts() {
|
loadCharts() {
|
||||||
this.loadMRRStats();
|
this.loadMRRStats();
|
||||||
this.loadMemberCountStats();
|
this.loadMemberCountStats();
|
||||||
|
this.loadNewsletterOpenRates();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadEvents() {
|
loadEvents() {
|
||||||
|
@ -143,6 +151,28 @@ export default class DashboardController extends Controller {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadNewsletterOpenRates() {
|
||||||
|
this.newsletterOpenRatesLoading = true;
|
||||||
|
this.membersStats.fetchNewsletterStats().then((results) => {
|
||||||
|
this.newsletterOpenRatesData = {
|
||||||
|
options: {
|
||||||
|
rangeInDays: 30
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
label: 'Open Rate',
|
||||||
|
dateLabels: results.map(d => d.submittedAt),
|
||||||
|
dateValues: results.map(d => d.openRate)
|
||||||
|
},
|
||||||
|
title: 'Open Rate',
|
||||||
|
stats: results
|
||||||
|
};
|
||||||
|
this.newsletterOpenRatesLoading = false;
|
||||||
|
}, (error) => {
|
||||||
|
this.newsletterOpenRatesError = error;
|
||||||
|
this.newsletterOpenRatesLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
loadTopMembers() {
|
loadTopMembers() {
|
||||||
this.topMembersLoading = true;
|
this.topMembersLoading = true;
|
||||||
let query = {
|
let query = {
|
||||||
|
|
|
@ -7,12 +7,14 @@ import {tracked} from '@glimmer/tracking';
|
||||||
export default class MembersStatsService extends Service {
|
export default class MembersStatsService extends Service {
|
||||||
@service ajax;
|
@service ajax;
|
||||||
@service ghostPaths;
|
@service ghostPaths;
|
||||||
|
@service store;
|
||||||
|
|
||||||
@tracked days = '30';
|
@tracked days = '30';
|
||||||
@tracked stats = null;
|
@tracked stats = null;
|
||||||
@tracked events = null;
|
@tracked events = null;
|
||||||
@tracked countStats = null;
|
@tracked countStats = null;
|
||||||
@tracked mrrStats = null;
|
@tracked mrrStats = null;
|
||||||
|
@tracked newsletterStats = null;
|
||||||
|
|
||||||
fetch() {
|
fetch() {
|
||||||
let daysChanged = this._lastFetchedDays !== this.days;
|
let daysChanged = this._lastFetchedDays !== this.days;
|
||||||
|
@ -61,6 +63,22 @@ export default class MembersStatsService extends Service {
|
||||||
return this._fetchCountsTask.perform();
|
return this._fetchCountsTask.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchNewsletterStats() {
|
||||||
|
let staleData = this._lastFetchedNewsletterStats && this._lastFetchedNewsletterStats - new Date() > 1 * 60 * 1000;
|
||||||
|
|
||||||
|
// return an already in-progress promise unless params have changed
|
||||||
|
if (this._fetchNewsletterStatsTask.isRunning) {
|
||||||
|
return this._fetchNewsletterStatsTask.last;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return existing stats unless data is > 1 min old
|
||||||
|
if (this.newsletterStats && !this._forceRefresh && !staleData) {
|
||||||
|
return Promise.resolve(this.countStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._fetchNewsletterStatsTask.perform();
|
||||||
|
}
|
||||||
|
|
||||||
fillDates(data) {
|
fillDates(data) {
|
||||||
let currentRangeDate = moment().subtract(30, 'days');
|
let currentRangeDate = moment().subtract(30, 'days');
|
||||||
|
|
||||||
|
@ -123,6 +141,27 @@ export default class MembersStatsService extends Service {
|
||||||
this._forceRefresh = true;
|
this._forceRefresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@task
|
||||||
|
*_fetchNewsletterStatsTask() {
|
||||||
|
let query = {
|
||||||
|
filter: 'email_count:-0',
|
||||||
|
order: 'submitted_at desc',
|
||||||
|
limit: 10
|
||||||
|
};
|
||||||
|
const results = yield this.store.query('email', query);
|
||||||
|
const stats = results.map((d) => {
|
||||||
|
const {emailCount, openedCount, subject, submittedAt} = d;
|
||||||
|
const openRate = (emailCount && emailCount !== 0) ? (openedCount / emailCount).toFixed(1) : 0;
|
||||||
|
return {
|
||||||
|
subject,
|
||||||
|
submittedAt: moment(submittedAt).format('YYYY-MM-DD'),
|
||||||
|
openRate
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.newsletterStats = stats;
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
@task
|
@task
|
||||||
*_fetchCountsTask() {
|
*_fetchCountsTask() {
|
||||||
this._lastFetchedCounts = new Date();
|
this._lastFetchedCounts = new Date();
|
||||||
|
|
|
@ -70,7 +70,9 @@
|
||||||
<div class="growth">0.0%</div>
|
<div class="growth">0.0%</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gh-dashboard-chart small"></div>
|
<div class="gh-dashboard-chart small">
|
||||||
|
<GhMembersChart @nightShift={{feature "nightShift"}} @chartSize="small" @showSummary={{false}} @chartType="paid-members" @showRange={{false}} @chartStats={{this.newsletterOpenRatesData}} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
Loading…
Add table
Reference in a new issue