mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 12:21:36 +03:00
Added member count trends in dashboard 5.0
refs https://github.com/TryGhost/Team/issues/1443 - Fetch member counts from 30 days ago - Added Percentage component to show +1%, -1% or = changes - Fixed: emails sent number was not formatted - Fixed: wrong dates were used for some stats
This commit is contained in:
parent
7d104b2b44
commit
8d3c1dacc2
@ -12,7 +12,7 @@
|
||||
|
||||
<div class="gh-dashboard5-box">
|
||||
<h4 class="gh-dashboard5-metric">Emails sent in the past 30 days</h4>
|
||||
<div class="gh-dashboard5-number is-small">{{this.dataEmailsSent}}</div>
|
||||
<div class="gh-dashboard5-number is-small">{{format-number this.dataEmailsSent}}</div>
|
||||
<div class="gh-dashboard5-percentage is-positive">+2.2%</div>
|
||||
</div>
|
||||
|
||||
|
@ -5,16 +5,22 @@
|
||||
<div class="gh-dashboard5-box">
|
||||
<h4 class="gh-dashboard5-metric">{{gh-pluralize this.totalMembers "Total member" without-count=true}}</h4>
|
||||
<div class="gh-dashboard5-number">{{format-number this.totalMembers}}</div>
|
||||
<div class="gh-dashboard5-percentage is-positive">+2.2%</div>
|
||||
{{#if this.hasTrends}}
|
||||
<Dashboard::v5::parts::ChartPercentage @percentage={{this.totalMembersTrend}}/>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="gh-dashboard5-box">
|
||||
<h4 class="gh-dashboard5-metric">{{gh-pluralize this.paidMembers "Paid member" without-count=true}}</h4>
|
||||
<div class="gh-dashboard5-number">{{format-number this.paidMembers}}</div>
|
||||
<div class="gh-dashboard5-percentage is-positive">+3.3%</div>
|
||||
{{#if this.hasTrends}}
|
||||
<Dashboard::v5::parts::ChartPercentage @percentage={{this.paidMembersTrend}}/>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="gh-dashboard5-box">
|
||||
<h4 class="gh-dashboard5-metric">{{gh-pluralize this.freeMembers "Free member" without-count=true}}</h4>
|
||||
<div class="gh-dashboard5-number">{{format-number this.freeMembers}}</div>
|
||||
<div class="gh-dashboard5-percentage is-negative">-1.1%</div>
|
||||
{{#if this.hasTrends}}
|
||||
<Dashboard::v5::parts::ChartPercentage @percentage={{this.freeMembersTrend}}/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
@ -21,4 +21,31 @@ export default class ChartMembersCounts extends Component {
|
||||
get freeMembers() {
|
||||
return this.dashboardStats.memberCounts?.free ?? 0;
|
||||
}
|
||||
|
||||
get hasTrends() {
|
||||
return this.dashboardStats.memberCounts !== null && this.dashboardStats.memberCountsTrend !== null;
|
||||
}
|
||||
|
||||
get totalMembersTrend() {
|
||||
return this.calculatePercentage(this.dashboardStats.memberCountsTrend.total, this.dashboardStats.memberCounts.total);
|
||||
}
|
||||
|
||||
get paidMembersTrend() {
|
||||
return this.calculatePercentage(this.dashboardStats.memberCountsTrend.paid, this.dashboardStats.memberCounts.paid);
|
||||
}
|
||||
|
||||
get freeMembersTrend() {
|
||||
return this.calculatePercentage(this.dashboardStats.memberCountsTrend.free, this.dashboardStats.memberCounts.free);
|
||||
}
|
||||
|
||||
calculatePercentage(from, to) {
|
||||
if (from === 0) {
|
||||
if (to > 0) {
|
||||
return 100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.round((to - from) / from * 100);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
{{#if (gt @percentage 0) }}
|
||||
<div class="gh-dashboard5-percentage is-positive">+{{ @percentage }}%</div>
|
||||
{{else if (lt @percentage 0)}}
|
||||
<div class="gh-dashboard5-percentage is-negative">{{ @percentage }}%</div>
|
||||
{{else}}
|
||||
<div class="gh-dashboard5-percentage">=</div>
|
||||
{{/if}}
|
@ -77,6 +77,12 @@ export default class DashboardStatsService extends Service {
|
||||
@tracked
|
||||
memberCounts = null;
|
||||
|
||||
/**
|
||||
* @type {?MemberCounts} Member counts to compare against for trends (30 days ago)
|
||||
*/
|
||||
@tracked
|
||||
memberCountsTrend = null;
|
||||
|
||||
/**
|
||||
* @type {?MemberCountStat[]}
|
||||
*/
|
||||
@ -171,27 +177,53 @@ export default class DashboardStatsService extends Service {
|
||||
@task({restartable: true})
|
||||
*_loadMembersCounts() {
|
||||
this.memberCounts = null;
|
||||
this.memberCountsTrend = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
if (this.dashboardMocks.memberCounts === null) {
|
||||
return null;
|
||||
}
|
||||
this.memberCounts = {...this.dashboardMocks.memberCounts};
|
||||
|
||||
this.memberCountsTrend = {
|
||||
// One percentage up
|
||||
total: Math.round(this.dashboardMocks.memberCounts.total * 1.06),
|
||||
// One percentage down
|
||||
free: Math.round(this.dashboardMocks.memberCounts.free * 0.96),
|
||||
// One percentage =
|
||||
paid: this.dashboardMocks.memberCounts.paid
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo We need to have way to reduce the total number of API requests
|
||||
const paidResult = yield this.store.query('member', {limit: 1, filter: 'status:paid'});
|
||||
const paid = paidResult.meta.pagination.total;
|
||||
let paidResult = yield this.store.query('member', {limit: 1, filter: 'status:paid'});
|
||||
let paid = paidResult.meta.pagination.total;
|
||||
|
||||
const freeResult = yield this.store.query('member', {limit: 1, filter: 'status:-paid'});
|
||||
const free = freeResult.meta.pagination.total;
|
||||
let freeResult = yield this.store.query('member', {limit: 1, filter: 'status:-paid'});
|
||||
let free = freeResult.meta.pagination.total;
|
||||
|
||||
this.memberCounts = {
|
||||
total: paid + free,
|
||||
paid,
|
||||
free
|
||||
};
|
||||
|
||||
// Now fetch trends (30 days ago)
|
||||
const trendDate = new Date(Date.now() - 30 * 60 * 60 * 24 * 1000);
|
||||
|
||||
paidResult = yield this.store.query('member', {limit: 1, filter: `status:paid+created_at:<'${trendDate.toISOString()}'`});
|
||||
paid = paidResult.meta.pagination.total;
|
||||
|
||||
freeResult = yield this.store.query('member', {limit: 1, filter: `status:-paid+created_at:<'${trendDate.toISOString()}'`});
|
||||
free = freeResult.meta.pagination.total;
|
||||
|
||||
this.memberCountsTrend = {
|
||||
total: paid + free,
|
||||
paid,
|
||||
free
|
||||
};
|
||||
}
|
||||
|
||||
loadMemberCountStats() {
|
||||
@ -286,8 +318,8 @@ export default class DashboardStatsService extends Service {
|
||||
|
||||
// @todo We need to have way to reduce the total number of API requests
|
||||
|
||||
const start30d = new Date(Date.now() - 30 * 3600 * 1000);
|
||||
const start7d = new Date(Date.now() - 7 * 3600 * 1000);
|
||||
const start30d = new Date(Date.now() - 30 * 86400 * 1000);
|
||||
const start7d = new Date(Date.now() - 7 * 86400 * 1000);
|
||||
|
||||
let extraFilter = '';
|
||||
if (this.lastSeenFilterStatus === 'paid') {
|
||||
@ -379,7 +411,7 @@ export default class DashboardStatsService extends Service {
|
||||
return;
|
||||
}
|
||||
|
||||
const start30d = new Date(Date.now() - 30 * 3600 * 1000);
|
||||
const start30d = new Date(Date.now() - 30 * 86400 * 1000);
|
||||
const result = yield this.store.query('email', {limit: 100, filter: 'submitted_at:>' + start30d.toISOString()});
|
||||
this.emailsSent30d = result.reduce((c, email) => c + email.emailCount, 0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user