Added async loading to dashboard 5 prototype charts
refs https://github.com/TryGhost/Team/issues/1443 - Also moved loading of charts outside of constructors to avoid infinite update loops in Ember - Random loading delay when using fake states
This commit is contained in:
parent
3c787a82b5
commit
9c593c9ea1
|
@ -1,4 +1,4 @@
|
|||
<h3>Email</h3>
|
||||
<h3 {{did-insert this.loadCharts}}>Email</h3>
|
||||
<div class="gh-dashboard5-container gh-dashboard5-email">
|
||||
<div class="gh-dashboard5-box">
|
||||
<h4 class="gh-dashboard5-metric">Newsletter subscribers</h4>
|
||||
|
|
|
@ -5,11 +5,6 @@ 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.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<h2>Engagement</h2>
|
||||
<h2 {{did-insert this.loadCharts}}>Engagement</h2>
|
||||
|
||||
<div class="prototype-selection">
|
||||
<PowerSelect
|
||||
|
|
|
@ -17,11 +17,7 @@ const STATUS_OPTIONS = [{
|
|||
export default class ChartEngagement extends Component {
|
||||
@service dashboardStats;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadCharts();
|
||||
}
|
||||
|
||||
@action
|
||||
loadCharts() {
|
||||
this.dashboardStats.lastSeenFilterStatus = this.status;
|
||||
this.dashboardStats.loadLastSeen();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<h3>Overview</h3>
|
||||
<h3 {{did-insert this.loadCharts}}>Overview</h3>
|
||||
<div class="gh-dashboard5-container">
|
||||
<div class="gh-dashboard5-box">
|
||||
<h4 class="gh-dashboard5-metric">{{gh-pluralize this.totalMembers "Total member" without-count=true}}</h4>
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class ChartMembersCounts extends Component {
|
||||
@service dashboardStats;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadCharts();
|
||||
}
|
||||
|
||||
@action
|
||||
loadCharts() {
|
||||
this.dashboardStats.loadMembersCounts();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<h4 class="gh-dashboard5-metric is-main">Monthly revenue (MMR)</h4>
|
||||
<h4
|
||||
class="gh-dashboard5-metric is-main"
|
||||
{{did-insert this.loadCharts}}
|
||||
{{did-update this.loadCharts @days}}
|
||||
>
|
||||
Monthly revenue (MMR)
|
||||
</h4>
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
|
|
|
@ -5,11 +5,6 @@ import {inject as service} from '@ember/service';
|
|||
export default class ChartMonthlyRevenue 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.
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<h4 class="gh-dashboard5-metric is-main">Paid members</h4>
|
||||
<h4
|
||||
class="gh-dashboard5-metric is-main"
|
||||
{{did-insert this.loadCharts}}
|
||||
{{did-update this.loadCharts @days}}
|
||||
>
|
||||
Paid members
|
||||
</h4>
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
|
|
|
@ -4,12 +4,7 @@ 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.
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</PowerSelect>
|
||||
</div>
|
||||
|
||||
<h4 class="gh-dashboard5-metric is-main">Paid mix</h4>
|
||||
<h4 class="gh-dashboard5-metric is-main" {{did-insert this.loadCharts}}>Paid mix</h4>
|
||||
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
|
|
|
@ -14,11 +14,6 @@ const MODE_OPTIONS = [{
|
|||
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.
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<h4 class="gh-dashboard5-metric is-main">Total members</h4>
|
||||
<h4
|
||||
class="gh-dashboard5-metric is-main"
|
||||
{{did-insert this.loadCharts}}
|
||||
{{did-update this.loadCharts @days}}
|
||||
>
|
||||
Total members
|
||||
</h4>
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
|
|
|
@ -5,11 +5,6 @@ import {inject as service} from '@ember/service';
|
|||
export default class ChartTotalMembers 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.
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<h4 class="gh-dashboard5-metric is-main">Total paid</h4>
|
||||
<h4
|
||||
class="gh-dashboard5-metric is-main"
|
||||
{{did-insert this.loadCharts}}
|
||||
{{did-update this.loadCharts @days}}
|
||||
>
|
||||
Total paid
|
||||
</h4>
|
||||
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart {{did-update this.loadCharts @days}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
|
|
|
@ -5,11 +5,6 @@ import {inject as service} from '@ember/service';
|
|||
export default class ChartTotalPaid 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.
|
||||
|
|
|
@ -53,7 +53,8 @@ export default class PrototypeControlPanel extends Component {
|
|||
@action
|
||||
onInsert() {
|
||||
this.loadState();
|
||||
this.updateMockedData();
|
||||
this.dashboardMocks.updateMockedData(this.state);
|
||||
// Don't reload all (because then we would load unused graphs too)
|
||||
}
|
||||
|
||||
saveState() {
|
||||
|
@ -80,7 +81,7 @@ export default class PrototypeControlPanel extends Component {
|
|||
const parsed = JSON.parse(savedStatus);
|
||||
if (parsed) {
|
||||
this.dashboardMocks.siteStatus = {...this.dashboardMocks.siteStatus, ...parsed};
|
||||
this.dashboardStats.loadSiteStatus();
|
||||
//this.dashboardStats.loadSiteStatus();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -87,10 +87,19 @@ export default class DashboardMocksService extends Service {
|
|||
@tracked
|
||||
emailOpenRateStats = null;
|
||||
|
||||
loadSiteStatus() {
|
||||
async waitRandom() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 100 + Math.random() * 1000);
|
||||
});
|
||||
}
|
||||
|
||||
async loadSiteStatus() {
|
||||
if (this.siteStatus !== null) {
|
||||
return;
|
||||
}
|
||||
await this.waitRandom();
|
||||
this.siteStatus = {
|
||||
hasPaidTiers: true,
|
||||
stripeEnabled: true,
|
||||
|
@ -175,15 +184,21 @@ export default class DashboardMocksService extends Service {
|
|||
this.paidMembersByTier = [
|
||||
{
|
||||
tier: {
|
||||
name: 'Gold tier'
|
||||
name: 'Bronze tier'
|
||||
},
|
||||
members: 124
|
||||
members: 985
|
||||
},
|
||||
{
|
||||
tier: {
|
||||
name: 'Silver tier'
|
||||
},
|
||||
members: 459
|
||||
},
|
||||
{
|
||||
tier: {
|
||||
name: 'Gold tier'
|
||||
},
|
||||
members: 124
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -201,11 +216,28 @@ export default class DashboardMocksService extends Service {
|
|||
this.emailOpenRateStats = [
|
||||
{
|
||||
id: '23424',
|
||||
title: 'Test post',
|
||||
title: '💸 The best way to get paid to create',
|
||||
email: {
|
||||
openedCount: 518,
|
||||
deliveredCount: 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '23425',
|
||||
title: '🎒How to start a blog and make money',
|
||||
email: {
|
||||
openedCount: 100,
|
||||
deliveredCount: 900
|
||||
}
|
||||
}
|
||||
,
|
||||
{
|
||||
id: '23426',
|
||||
title: 'How to turn your amateur blogging into a real business',
|
||||
email: {
|
||||
openedCount: 500,
|
||||
deliveredCount: 1600
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Service, {inject as service} from '@ember/service';
|
||||
import moment from 'moment';
|
||||
import {task} from 'ember-concurrency';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
/**
|
||||
|
@ -140,11 +141,15 @@ export default class DashboardStatsService extends Service {
|
|||
@tracked lastSeenFilterStatus = 'total';
|
||||
|
||||
loadSiteStatus() {
|
||||
return this._loadSiteStatus.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*_loadSiteStatus() {
|
||||
this.siteStatus = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.dashboardMocks.loadSiteStatus();
|
||||
this.siteStatus = this.dashboardMocks.siteStatus;
|
||||
yield this.dashboardMocks.loadSiteStatus();
|
||||
this.siteStatus = {...this.dashboardMocks.siteStatus};
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
|
@ -152,7 +157,14 @@ export default class DashboardStatsService extends Service {
|
|||
}
|
||||
|
||||
loadMembersCounts() {
|
||||
return this._loadMembersCounts.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*_loadMembersCounts() {
|
||||
this.memberCounts = null;
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
if (this.dashboardMocks.memberCounts === null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -163,15 +175,25 @@ export default class DashboardStatsService extends Service {
|
|||
// @todo
|
||||
}
|
||||
|
||||
loadMemberCountStats() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadMemberCountStats.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the members graphs
|
||||
* - total paid
|
||||
* - total members
|
||||
* for each day in the last chartDays days
|
||||
*/
|
||||
loadMemberCountStats() {
|
||||
@task
|
||||
*_loadMemberCountStats() {
|
||||
this.memberCountStats = null;
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
|
||||
if (this.dashboardMocks.memberCountStats === null) {
|
||||
// Note: that this shouldn't happen
|
||||
return null;
|
||||
}
|
||||
this.memberCountStats = this.fillMissingDates(this.dashboardMocks.memberCountStats.slice(-this.chartDays), {paid: 0, free: 0, comped: 0}, this.chartDays);
|
||||
|
@ -182,11 +204,19 @@ export default class DashboardStatsService extends Service {
|
|||
// @todo
|
||||
}
|
||||
|
||||
loadMrrStats() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadMrrStats.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the mrr graphs for the current chartDays days
|
||||
*/
|
||||
loadMrrStats() {
|
||||
@task
|
||||
*_loadMrrStats() {
|
||||
this.mrrStats = null;
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
if (this.dashboardMocks.mrrStats === null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -198,11 +228,21 @@ export default class DashboardStatsService extends Service {
|
|||
// @todo
|
||||
}
|
||||
|
||||
loadLastSeen() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadLastSeen.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the mrr graphs
|
||||
*/
|
||||
loadLastSeen() {
|
||||
@task
|
||||
*_loadLastSeen() {
|
||||
this.membersLastSeen30d = null;
|
||||
this.membersLastSeen7d = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
if (this.lastSeenFilterStatus === 'paid') {
|
||||
// @todo
|
||||
}
|
||||
|
@ -215,8 +255,17 @@ export default class DashboardStatsService extends Service {
|
|||
}
|
||||
|
||||
loadPaidMembersByCadence() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadPaidMembersByCadence.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*_loadPaidMembersByCadence() {
|
||||
this.paidMembersByCadence = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.paidMembersByCadence = this.dashboardMocks.paidMembersByCadence;
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
this.paidMembersByCadence = {...this.dashboardMocks.paidMembersByCadence};
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
|
@ -224,8 +273,17 @@ export default class DashboardStatsService extends Service {
|
|||
}
|
||||
|
||||
loadPaidMembersByTier() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadPaidMembersByTier.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*_loadPaidMembersByTier() {
|
||||
this.paidMembersByTier = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
this.paidMembersByTier = this.dashboardMocks.paidMembersByTier;
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
this.paidMembersByTier = this.dashboardMocks.paidMembersByTier.slice();
|
||||
return;
|
||||
}
|
||||
// Normal implementation
|
||||
|
@ -233,7 +291,16 @@ export default class DashboardStatsService extends Service {
|
|||
}
|
||||
|
||||
loadNewsletterSubscribers() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadNewsletterSubscribers.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*_loadNewsletterSubscribers() {
|
||||
this.newsletterSubscribers = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
this.newsletterSubscribers = this.dashboardMocks.newsletterSubscribers;
|
||||
return;
|
||||
}
|
||||
|
@ -242,7 +309,16 @@ export default class DashboardStatsService extends Service {
|
|||
}
|
||||
|
||||
loadEmailsSent() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadEmailsSent.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*_loadEmailsSent() {
|
||||
this.emailsSent30d = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
this.emailsSent30d = this.dashboardMocks.emailsSent30d;
|
||||
return;
|
||||
}
|
||||
|
@ -251,7 +327,16 @@ export default class DashboardStatsService extends Service {
|
|||
}
|
||||
|
||||
loadEmailOpenRateStats() {
|
||||
// todo: add proper logic to prevent duplicate calls + reuse results if nothing has changed
|
||||
return this._loadEmailOpenRateStats.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*_loadEmailOpenRateStats() {
|
||||
this.emailOpenRateStats = null;
|
||||
|
||||
if (this.dashboardMocks.enabled) {
|
||||
yield this.dashboardMocks.waitRandom();
|
||||
this.emailOpenRateStats = this.dashboardMocks.emailOpenRateStats;
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue