mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-29 07:09:48 +03:00
Wired all the dashboard 5.0 charts with the stats service
refs https://github.com/TryGhost/Team/issues/1442 All the data from the charts now come from the dashboard stats service
This commit is contained in:
parent
101ebadc47
commit
159f56b0d2
@ -30,6 +30,11 @@ export default class DashboardDashboardV5Component extends Component {
|
||||
|
||||
daysOptions = DAYS_OPTIONS;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
//this.dashboardMocks.updateMockedData({days: 14});
|
||||
}
|
||||
|
||||
get selectedDaysOption() {
|
||||
return this.daysOptions.find(d => d.value === this.days);
|
||||
}
|
||||
|
@ -15,11 +15,15 @@
|
||||
|
||||
<div class="prototype-box">
|
||||
<h4>Email open rate</h4>
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{#if this.loading}}
|
||||
<div class="prototype-placeholder" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,12 +1,38 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class ChartEmailOpenRate extends Component {
|
||||
@service dashboardStats;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadCharts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when you need to fetch new data from the server. In this component, it will get called
|
||||
* when the days parameter changes and on initialisation.
|
||||
*/
|
||||
@action
|
||||
loadCharts() {
|
||||
// The dashboard stats service will take care or reusing and limiting API-requests between charts
|
||||
this.dashboardStats.loadNewsletterSubscribers();
|
||||
this.dashboardStats.loadEmailsSent();
|
||||
this.dashboardStats.loadEmailOpenRateStats();
|
||||
}
|
||||
|
||||
get dataSubscribers() {
|
||||
return '9,250';
|
||||
// @todo: show paid, free, total together
|
||||
return this.dashboardStats.newsletterSubscribers?.total ?? 0;
|
||||
}
|
||||
|
||||
get dataEmailsSent() {
|
||||
return '40.3k';
|
||||
return this.dashboardStats.emailsSent30d ?? 0;
|
||||
}
|
||||
|
||||
get loading() {
|
||||
return this.dashboardStats.emailOpenRateStats === null;
|
||||
}
|
||||
|
||||
get chartType() {
|
||||
@ -14,10 +40,14 @@ export default class ChartEmailOpenRate extends Component {
|
||||
}
|
||||
|
||||
get chartData() {
|
||||
const stats = this.dashboardStats.emailOpenRateStats.filter(stat => stat.email.deliveredCount > 0);
|
||||
const labels = stats.map(stat => stat.title);
|
||||
const data = stats.map(stat => stat.email.openedCount / stat.email.deliveredCount * 100);
|
||||
|
||||
return {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June'],
|
||||
labels,
|
||||
datasets: [{
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
data,
|
||||
fill: false,
|
||||
backgroundColor: '#14b8ff',
|
||||
tension: 0.1
|
||||
|
@ -1,4 +1,4 @@
|
||||
<h2>Engagement</h2>
|
||||
<h2>Engagement ({{this.status}})</h2>
|
||||
|
||||
<div class="prototype-counts">
|
||||
<div class="prototype-box">
|
||||
|
@ -1,11 +1,59 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class ChartEngagement extends Component {
|
||||
@service dashboardStats;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadCharts();
|
||||
}
|
||||
|
||||
loadCharts() {
|
||||
this.dashboardStats.loadLastSeen(this.status);
|
||||
this.dashboardStats.loadMembersCounts();
|
||||
}
|
||||
|
||||
get status() {
|
||||
// todo: this should come from a dropdown
|
||||
// + reload stats after changing this value
|
||||
return 'total';
|
||||
}
|
||||
|
||||
get loading() {
|
||||
return this.dashboardStats.memberCounts === null
|
||||
|| !this.dashboardStats.memberCounts[this.status]
|
||||
|| this.dashboardStats.membersLastSeen30d === null
|
||||
|| this.dashboardStats.membersLastSeen7d === null;
|
||||
}
|
||||
|
||||
get data30Days() {
|
||||
return '67%';
|
||||
if (this.loading) {
|
||||
return '- %';
|
||||
}
|
||||
const total = this.dashboardStats.memberCounts[this.status];
|
||||
const part = this.dashboardStats.membersLastSeen30d;
|
||||
|
||||
if (total <= 0) {
|
||||
return '- %';
|
||||
}
|
||||
|
||||
const percentage = Math.round(part / total * 100);
|
||||
return `${percentage}%`;
|
||||
}
|
||||
|
||||
get data7Days() {
|
||||
return '31%';
|
||||
if (this.loading) {
|
||||
return '- %';
|
||||
}
|
||||
const total = this.dashboardStats.memberCounts[this.status];
|
||||
const part = this.dashboardStats.membersLastSeen7d;
|
||||
|
||||
if (total <= 0) {
|
||||
return '- %';
|
||||
}
|
||||
|
||||
const percentage = Math.round(part / total * 100);
|
||||
return `${percentage}%`;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
<h4>MRR</h4>
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{#if this.loading}}
|
||||
<div class="prototype-placeholder" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{/if}}
|
@ -20,6 +20,10 @@ export default class ChartMonthlyRevenue extends Component {
|
||||
this.dashboardStats.loadMrrStats(this.args.days);
|
||||
}
|
||||
|
||||
get loading() {
|
||||
return this.dashboardStats.mrrStats === null;
|
||||
}
|
||||
|
||||
get chartType() {
|
||||
return 'line';
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
<h4>Paid members</h4>
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{#if this.loading}}
|
||||
<div class="prototype-placeholder" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{/if}}
|
@ -1,21 +1,49 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class ChartPaidMembers extends Component {
|
||||
@service dashboardStats;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadCharts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when you need to fetch new data from the server. In this component, it will get called
|
||||
* when the days parameter changes and on initialisation.
|
||||
*/
|
||||
@action
|
||||
loadCharts() {
|
||||
// The dashboard stats service will take care or reusing and limiting API-requests between charts
|
||||
this.dashboardStats.loadMemberCountStats(this.args.days);
|
||||
}
|
||||
|
||||
get loading() {
|
||||
return this.dashboardStats.memberCountStats === null;
|
||||
}
|
||||
|
||||
get chartType() {
|
||||
return 'bar';
|
||||
}
|
||||
|
||||
get chartData() {
|
||||
const stats = this.dashboardStats.memberCountStats;
|
||||
const labels = stats.map(stat => stat.date);
|
||||
const newData = stats.map(stat => stat.newPaid);
|
||||
const canceledData = stats.map(stat => -stat.canceledPaid);
|
||||
|
||||
return {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June'],
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
data: newData,
|
||||
fill: false,
|
||||
backgroundColor: '#14b8ff',
|
||||
tension: 0.1
|
||||
},{
|
||||
data: [-65, -59, -80, -81, -56, -55, -40],
|
||||
data: canceledData,
|
||||
fill: false,
|
||||
backgroundColor: '#E16262',
|
||||
tension: 0.1
|
||||
|
@ -1,6 +1,10 @@
|
||||
<h4>Paid mix</h4>
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
<h4>Paid mix ({{this.mode}})</h4>
|
||||
{{#if this.loading}}
|
||||
<div class="prototype-placeholder" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{/if}}
|
@ -1,20 +1,57 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class ChartPaidMix extends Component {
|
||||
@service dashboardStats;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadCharts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when you need to fetch new data from the server. In this component, it will get called
|
||||
* when the days parameter changes and on initialisation.
|
||||
*/
|
||||
@action
|
||||
loadCharts() {
|
||||
// The dashboard stats service will take care or reusing and limiting API-requests between charts
|
||||
if (this.mode === 'cadence') {
|
||||
this.dashboardStats.loadPaidMembersByCadence();
|
||||
} else {
|
||||
this.dashboardStats.loadPaidMembersByTier();
|
||||
}
|
||||
}
|
||||
|
||||
get loading() {
|
||||
if (this.mode === 'cadence') {
|
||||
return this.dashboardStats.paidMembersByCadence === null;
|
||||
}
|
||||
return this.dashboardStats.paidMembersByTier === null;
|
||||
}
|
||||
|
||||
get mode() {
|
||||
return 'cadence';
|
||||
}
|
||||
|
||||
get chartType() {
|
||||
return 'pie';
|
||||
}
|
||||
|
||||
get chartData() {
|
||||
return {
|
||||
labels: ['Monthly', 'Annual'],
|
||||
datasets: [{
|
||||
data: [20, 80],
|
||||
fill: false,
|
||||
backgroundColor: ['#14b8ff'],
|
||||
tension: 0.1
|
||||
}]
|
||||
};
|
||||
if (this.mode === 'cadence') {
|
||||
return {
|
||||
labels: ['Monthly', 'Annual'],
|
||||
datasets: [{
|
||||
data: [this.dashboardStats.paidMembersByCadence.monthly, this.dashboardStats.paidMembersByCadence.annual],
|
||||
fill: false,
|
||||
backgroundColor: ['#14b8ff'],
|
||||
tension: 0.1
|
||||
}]
|
||||
};
|
||||
}
|
||||
throw new Error('Not yet supported');
|
||||
}
|
||||
|
||||
get chartOptions() {
|
||||
|
@ -1,6 +1,10 @@
|
||||
<h4>Total members</h4>
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{#if this.loading}}
|
||||
<div class="prototype-placeholder" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{/if}}
|
@ -19,6 +19,10 @@ export default class ChartTotalMembers extends Component {
|
||||
// The dashboard stats service will take care or reusing and limiting API-requests between charts
|
||||
this.dashboardStats.loadMemberCountStats(this.args.days);
|
||||
}
|
||||
|
||||
get loading() {
|
||||
return this.dashboardStats.memberCountStats === null;
|
||||
}
|
||||
|
||||
get chartType() {
|
||||
return 'line';
|
||||
|
@ -1,6 +1,10 @@
|
||||
<h4>Total paid</h4>
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{#if this.loading}}
|
||||
<div class="prototype-placeholder" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{/if}}
|
@ -20,6 +20,10 @@ export default class ChartTotalPaid extends Component {
|
||||
this.dashboardStats.loadMemberCountStats(this.args.days);
|
||||
}
|
||||
|
||||
get loading() {
|
||||
return this.dashboardStats.memberCountStats === null;
|
||||
}
|
||||
|
||||
get chartType() {
|
||||
return 'line';
|
||||
}
|
||||
|
@ -1,6 +1,15 @@
|
||||
import Service from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
/**
|
||||
* @typedef {import('./dashboard-stats').MemberCountStat} MemberCountStat
|
||||
* @typedef {import('./dashboard-stats').MemberCounts} MemberCounts
|
||||
* @typedef {import('./dashboard-stats').MrrStat} MrrStat
|
||||
* @typedef {import('./dashboard-stats').EmailOpenRateStat} EmailOpenRateStat
|
||||
* @typedef {import('./dashboard-stats').PaidMembersByCadence} PaidMembersByCadence
|
||||
* @typedef {import('./dashboard-stats').PaidMembersForTier} PaidMembersForTier
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service that contains fake data to be used by the DashboardStatsService if useMocks is enabled
|
||||
*/
|
||||
@ -8,27 +17,63 @@ export default class DashboardMocksService extends Service {
|
||||
@tracked
|
||||
enabled = true;
|
||||
|
||||
/**
|
||||
* @type {?MemberCounts} memberCounts
|
||||
*/
|
||||
@tracked
|
||||
memberCounts = null;
|
||||
|
||||
/**
|
||||
* @type {?MemberCountStat[]}
|
||||
*/
|
||||
@tracked
|
||||
memberCountStats = [];
|
||||
memberCountStats = null;
|
||||
|
||||
/**
|
||||
* @type {?MrrStat[]}
|
||||
*/
|
||||
@tracked
|
||||
mrrStats = [];
|
||||
mrrStats = null;
|
||||
|
||||
/**
|
||||
* @type {PaidMembersByCadence} Number of members for annual and monthly plans
|
||||
*/
|
||||
@tracked
|
||||
membersLastSeen30d = 123;
|
||||
paidMembersByCadence = null;
|
||||
|
||||
/**
|
||||
* @type {PaidMembersForTier[]} Number of members for each tier
|
||||
*/
|
||||
@tracked
|
||||
membersLastSeen7d = 51;
|
||||
paidMembersByTier = null;
|
||||
|
||||
/**
|
||||
* @type {?number} Number of members last seen in last 30 days (could differ if filtered by member status)
|
||||
*/
|
||||
@tracked
|
||||
membersLastSeen30d = null;
|
||||
|
||||
/**
|
||||
* @type {?number} Number of members last seen in last 7 days (could differ if filtered by member status)
|
||||
*/
|
||||
@tracked
|
||||
membersLastSeen7d = null;
|
||||
|
||||
/**
|
||||
* @type {?MemberCounts} Number of members that are subscribed (grouped by status)
|
||||
*/
|
||||
@tracked
|
||||
newsletterSubscribers = null;
|
||||
|
||||
/**
|
||||
* @type {?number} Number of emails sent in last 30 days
|
||||
*/
|
||||
@tracked
|
||||
emailsSent30d = null;
|
||||
|
||||
/**
|
||||
* @type {?EmailOpenRateStat[]}
|
||||
*/
|
||||
@tracked
|
||||
emailOpenRateStats = null;
|
||||
|
||||
@ -41,6 +86,9 @@ export default class DashboardMocksService extends Service {
|
||||
const startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() - generateDays + 1);
|
||||
|
||||
/**
|
||||
* @type {MemberCountStat[]}
|
||||
*/
|
||||
const stats = [];
|
||||
let growPeriod = true;
|
||||
let growCount = 0;
|
||||
@ -53,11 +101,14 @@ export default class DashboardMocksService extends Service {
|
||||
|
||||
const previous = stats.length ? stats[stats.length - 1] : {free: 0, paid: 0, comped: 0};
|
||||
|
||||
const paid = index === 0 ? 0 : Math.max(0, previous.paid + Math.round(Math.random() * (growRate - 3)));
|
||||
stats.push({
|
||||
date: date.toISOString().split('T')[0],
|
||||
free: index === 0 ? 0 : Math.max(0, previous.free + Math.floor(Math.random() * (growRate))),
|
||||
paid: index === 0 ? 0 : Math.max(0, previous.paid + Math.floor(Math.random() * (growRate - 3))),
|
||||
comped: 0
|
||||
free: index === 0 ? 0 : Math.max(0, previous.free + Math.round(Math.random() * (growRate))),
|
||||
paid,
|
||||
comped: 0,
|
||||
newPaid: Math.max(paid - previous.paid + 5, 0),
|
||||
canceledPaid: Math.max(previous.paid - paid, 0) + 5
|
||||
});
|
||||
|
||||
if (growPeriod) {
|
||||
@ -94,6 +145,48 @@ export default class DashboardMocksService extends Service {
|
||||
free: stats[stats.length - 1].free + stats[stats.length - 1].comped
|
||||
};
|
||||
|
||||
this.paidMembersByCadence = {
|
||||
annual: 546,
|
||||
monthly: 5162
|
||||
};
|
||||
|
||||
this.paidMembersByTier = [
|
||||
{
|
||||
tier: {
|
||||
name: 'Gold tier'
|
||||
},
|
||||
members: 124
|
||||
},
|
||||
{
|
||||
tier: {
|
||||
name: 'Silver tier'
|
||||
},
|
||||
members: 459
|
||||
}
|
||||
];
|
||||
|
||||
this.newsletterSubscribers = {
|
||||
paid: 156,
|
||||
free: 8459,
|
||||
total: 156 + 8459
|
||||
};
|
||||
|
||||
this.emailsSent30d = 123;
|
||||
|
||||
this.membersLastSeen7d = Math.round(Math.random() * this.memberCounts.free / 2);
|
||||
this.membersLastSeen30d = this.membersLastSeen7d + Math.round(Math.random() * this.memberCounts.free / 2);
|
||||
|
||||
this.emailOpenRateStats = [
|
||||
{
|
||||
id: '23424',
|
||||
title: 'Test post',
|
||||
email: {
|
||||
openedCount: 518,
|
||||
deliveredCount: 1234
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.mrrStats = stats.map((s) => {
|
||||
return {
|
||||
date: s.date,
|
||||
|
@ -2,36 +2,121 @@ import Service, {inject as service} from '@ember/service';
|
||||
import moment from 'moment';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
/**
|
||||
* @typedef MrrStat
|
||||
* @type {Object}
|
||||
* @property {string} date The date (YYYY-MM-DD) on which this MRR was recorded
|
||||
* @property {number} mrr The MRR on this date
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef MemberCountStat
|
||||
* @type {Object}
|
||||
* @property {string} date The date (YYYY-MM-DD) on which these counts were recorded
|
||||
* @property {number} paid Amount of paid members
|
||||
* @property {number} free Amount of free members
|
||||
* @property {number} comped Amount of comped members
|
||||
* @property {number} newPaid Amount of new paid members
|
||||
* @property {number} canceledPaid Amount of canceled paid members
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef MemberCounts
|
||||
* @type {Object}
|
||||
* @property {number} total Total amount of members
|
||||
* @property {number} paid Amount of paid members
|
||||
* @property {number} free Amount of free members
|
||||
*/
|
||||
|
||||
/**
|
||||
* @todo: THIS ONE IS TEMPORARY
|
||||
* @typedef EmailOpenRateStat (Will be the same as post model probably)
|
||||
* @type {Object}
|
||||
* @property {string} id Post id
|
||||
* @property {string} title Post title
|
||||
* @property {?Object} Email model
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef PaidMembersByCadence
|
||||
* @type {Object}
|
||||
* @property {number} annual Paid memebrs on annual plan
|
||||
* @property {number} monthly Paid memebrs on monthly plan
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef PaidMembersForTier
|
||||
* @type {Object}
|
||||
* @property {Object} tier Tier object
|
||||
* @property {number} members Paid members on this tier
|
||||
*/
|
||||
|
||||
export default class DashboardStatsService extends Service {
|
||||
@service dashboardMocks;
|
||||
|
||||
/**
|
||||
* @type {?MemberCounts} memberCounts
|
||||
*/
|
||||
@tracked
|
||||
memberCounts = null;
|
||||
|
||||
/**
|
||||
* @type {?MemberCountStat[]}
|
||||
*/
|
||||
@tracked
|
||||
memberCountStats = [];
|
||||
memberCountStats = null;
|
||||
|
||||
/**
|
||||
* @type {?MrrStat[]}
|
||||
*/
|
||||
@tracked
|
||||
mrrStats = [];
|
||||
mrrStats = null;
|
||||
|
||||
/**
|
||||
* @type {PaidMembersByCadence} Number of members for annual and monthly plans
|
||||
*/
|
||||
@tracked
|
||||
paidMembersByCadence = null;
|
||||
|
||||
/**
|
||||
* @type {PaidMembersForTier[]} Number of members for each tier
|
||||
*/
|
||||
@tracked
|
||||
paidMembersByTier = null;
|
||||
|
||||
/**
|
||||
* @type {?number} Number of members last seen in last 30 days (could differ if filtered by member status)
|
||||
*/
|
||||
@tracked
|
||||
membersLastSeen30d = null;
|
||||
|
||||
/**
|
||||
* @type {?number} Number of members last seen in last 7 days (could differ if filtered by member status)
|
||||
*/
|
||||
@tracked
|
||||
membersLastSeen7d = null;
|
||||
|
||||
/**
|
||||
* @type {?MemberCounts} Number of members that are subscribed (grouped by status)
|
||||
*/
|
||||
@tracked
|
||||
newsletterSubscribers = null;
|
||||
|
||||
/**
|
||||
* @type {?number} Number of emails sent in last 30 days
|
||||
*/
|
||||
@tracked
|
||||
emailsSent30d = null;
|
||||
|
||||
/**
|
||||
* @type {?EmailOpenRateStat[]}
|
||||
*/
|
||||
@tracked
|
||||
emailOpenRateStats = null;
|
||||
|
||||
loadMembersCounts() {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.memberCounts = this.dashboardMocks.memberCounts;
|
||||
this.memberCounts = {...this.dashboardMocks.memberCounts};
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
@ -47,7 +132,10 @@ export default class DashboardStatsService extends Service {
|
||||
*/
|
||||
loadMemberCountStats(days) {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.memberCountStats = this.fillMissingDates(this.dashboardMocks.memberCountStats.slice(-days), {paid: 0,free: 0,comped: 0}, days);
|
||||
if (this.dashboardMocks.memberCountStats === null) {
|
||||
return null;
|
||||
}
|
||||
this.memberCountStats = this.fillMissingDates(this.dashboardMocks.memberCountStats.slice(-days), {paid: 0, free: 0, comped: 0}, days);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -61,6 +149,9 @@ export default class DashboardStatsService extends Service {
|
||||
*/
|
||||
loadMrrStats(days) {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
if (this.dashboardMocks.mrrStats === null) {
|
||||
return null;
|
||||
}
|
||||
this.mrrStats = this.fillMissingDates(this.dashboardMocks.mrrStats.slice(-days), {mrr: 0}, days);
|
||||
return;
|
||||
}
|
||||
@ -69,8 +160,15 @@ export default class DashboardStatsService extends Service {
|
||||
// @todo
|
||||
}
|
||||
|
||||
loadLastSeen() {
|
||||
/**
|
||||
* Loads the mrr graphs
|
||||
* @param {'paid'|'free'|'total'} status filter by status
|
||||
*/
|
||||
loadLastSeen(status) {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
if (status === 'paid') {
|
||||
// @todo
|
||||
}
|
||||
this.membersLastSeen30d = this.dashboardMocks.membersLastSeen30d;
|
||||
this.membersLastSeen7d = this.dashboardMocks.membersLastSeen7d;
|
||||
return;
|
||||
@ -79,19 +177,74 @@ export default class DashboardStatsService extends Service {
|
||||
// @todo
|
||||
}
|
||||
|
||||
loadPaidMembersByCadence() {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.paidMembersByCadence = this.dashboardMocks.paidMembersByCadence;
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
// @todo
|
||||
}
|
||||
|
||||
loadPaidMembersByTier() {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.paidMembersByTier = this.dashboardMocks.paidMembersByTier;
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
// @todo
|
||||
}
|
||||
|
||||
loadNewsletterSubscribers() {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.newsletterSubscribers = this.dashboardMocks.newsletterSubscribers;
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
// @todo
|
||||
}
|
||||
|
||||
loadEmailsSent() {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.emailsSent30d = this.dashboardMocks.emailsSent30d;
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
// @todo
|
||||
}
|
||||
|
||||
loadEmailOpenRateStats() {
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.emailOpenRateStats = this.dashboardMocks.emailOpenRateStats;
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
// @todo
|
||||
}
|
||||
|
||||
/**
|
||||
* For now this is only used when reloading all the graphs after changing the mocked data
|
||||
* @todo: reload only data that we loaded earlier
|
||||
* @param {number} days Amount of days to load data for (used for member related graphs)
|
||||
*/
|
||||
reloadAll(days) {
|
||||
this.loadMembersCounts();
|
||||
this.loadMrrStats(days);
|
||||
this.loadMemberCountStats(days);
|
||||
this.loadLastSeen();
|
||||
this.loadLastSeen('paid');
|
||||
this.loadPaidMembersByCadence();
|
||||
this.loadPaidMembersByTier();
|
||||
|
||||
this.loadNewsletterSubscribers();
|
||||
this.loadEmailsSent();
|
||||
this.loadEmailOpenRateStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill data to match a given amount of days
|
||||
* @param {MemberCountStat[]|MrrStat[]} data
|
||||
* @param {MemberCountStat|MrrStat} defaultData
|
||||
* @param {number} days Amount of days to fill the graph with
|
||||
*/
|
||||
fillMissingDates(data, defaultData, days) {
|
||||
let currentRangeDate = moment().subtract(days, 'days');
|
||||
@ -115,6 +268,7 @@ export default class DashboardStatsService extends Service {
|
||||
let dateStr = currentRangeDate.format('YYYY-MM-DD');
|
||||
const dataOnDate = data.find(d => d.date === dateStr);
|
||||
lastVal = dataOnDate ? dataOnDate : lastVal;
|
||||
lastVal.date = dateStr;
|
||||
output.push(lastVal);
|
||||
currentRangeDate = currentRangeDate.add(1, 'day');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user