Ghost/ghost/admin/app/controllers/dashboard.js
Rishabh Garg 208b4948ff Moved launch complete flag to new editor setting (#2135)
refs https://github.com/TryGhost/Team/issues/807
refs https://github.com/TryGhost/Ghost/pull/13703

The launch wizard completed flag was previously stored at per user level in accessibility column of user table, so an administrator still got the option to complete the launch wizard even if the owner had completed it previously, which is not expected pattern. This change moves the launch complete flag to be stored in new global setting for site.
2021-11-04 18:04:39 +05:30

242 lines
9.3 KiB
JavaScript

import Controller from '@ember/controller';
import {action} from '@ember/object';
import {getSymbol} from 'ghost-admin/utils/currency';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';
export default class DashboardController extends Controller {
@service feature;
@service session;
@service membersStats;
@service store;
@service settings;
@service whatsNew;
@tracked eventsData = null;
@tracked eventsError = null;
@tracked eventsLoading = false;
@tracked mrrStatsData = null;
@tracked mrrStatsError = null;
@tracked mrrStatsLoading = false;
@tracked memberCountStatsData = null;
@tracked memberCountStatsError = null;
@tracked memberCountStatsLoading = false;
@tracked topMembersData = null;
@tracked topMembersError = null;
@tracked topMembersLoading = false;
@tracked newsletterOpenRatesData = null;
@tracked newsletterOpenRatesError = null;
@tracked newsletterOpenRatesLoading = false;
@tracked whatsNewEntries = null;
@tracked whatsNewEntriesLoading = null;
@tracked whatsNewEntriesError = null;
get topMembersDataHasOpenRates() {
return this.topMembersData && this.topMembersData.find((member) => {
return member.emailOpenRate !== null;
});
}
get showMembersData() {
return this.settings.get('membersSignupAccess') !== 'none';
}
initialise() {
this.loadEvents();
this.loadTopMembers();
this.loadCharts();
this.loadWhatsNew();
}
async loadMRRStats() {
const products = await this.store.query('product', {include: 'monthly_price,yearly_price', limit: 'all'});
const defaultProduct = products?.firstObject;
this.mrrStatsLoading = true;
this.membersStats.fetchMRR().then((stats) => {
this.mrrStatsLoading = false;
const statsData = stats.data || [];
const defaultCurrency = defaultProduct?.monthlyPrice?.currency || 'usd';
let currencyStats = statsData.find((stat) => {
return stat.currency === defaultCurrency;
});
currencyStats = currencyStats || {
data: [],
currency: defaultCurrency
};
if (currencyStats) {
const currencyStatsData = this.membersStats.fillDates(currencyStats.data) || {};
const dateValues = Object.values(currencyStatsData).map(val => Math.round((val / 100)));
const currentMRR = dateValues.length ? dateValues[dateValues.length - 1] : 0;
const rangeStartMRR = dateValues.length ? dateValues[0] : 0;
const percentGrowth = rangeStartMRR !== 0 ? ((currentMRR - rangeStartMRR) / rangeStartMRR) * 100 : 0;
this.mrrStatsData = {
currentAmount: currentMRR,
currency: getSymbol(currencyStats.currency),
percentGrowth: percentGrowth.toFixed(1),
percentClass: (percentGrowth > 0 ? 'positive' : (percentGrowth < 0 ? 'negative' : '')),
options: {
rangeInDays: 30
},
data: {
label: 'MRR',
dateLabels: Object.keys(currencyStatsData),
dateValues
},
title: 'MRR',
stats: currencyStats
};
}
}, (error) => {
this.mrrStatsError = error;
this.mrrStatsLoading = false;
});
}
loadMemberCountStats() {
this.memberCountStatsLoading = true;
this.membersStats.fetchCounts().then((stats) => {
this.memberCountStatsLoading = false;
if (stats) {
const statsDateObj = this.membersStats.fillCountDates(stats.data) || {};
const dateValues = Object.values(statsDateObj);
const currentAllCount = dateValues.length ? dateValues[dateValues.length - 1].total : 0;
const currentPaidCount = dateValues.length ? dateValues[dateValues.length - 1].paid : 0;
const rangeStartAllCount = dateValues.length ? dateValues[0].total : 0;
const rangeStartPaidCount = dateValues.length ? dateValues[0].paid : 0;
const allCountPercentGrowth = rangeStartAllCount !== 0 ? ((currentAllCount - rangeStartAllCount) / rangeStartAllCount) * 100 : 0;
const paidCountPercentGrowth = rangeStartPaidCount !== 0 ? ((currentPaidCount - rangeStartPaidCount) / rangeStartPaidCount) * 100 : 0;
this.memberCountStatsData = {
all: {
percentGrowth: allCountPercentGrowth.toFixed(1),
percentClass: (allCountPercentGrowth > 0 ? 'positive' : (allCountPercentGrowth < 0 ? 'negative' : '')),
total: dateValues.length ? dateValues[dateValues.length - 1].total : 0,
options: {
rangeInDays: 30
},
data: {
label: 'Members',
dateLabels: Object.keys(statsDateObj),
dateValues: dateValues.map(d => d.total)
},
title: 'Total Members',
stats: stats
},
paid: {
percentGrowth: paidCountPercentGrowth.toFixed(1),
percentClass: (paidCountPercentGrowth > 0 ? 'positive' : (paidCountPercentGrowth < 0 ? 'negative' : '')),
total: dateValues.length ? dateValues[dateValues.length - 1].paid : 0,
options: {
rangeInDays: 30
},
data: {
label: 'Members',
dateLabels: Object.keys(statsDateObj),
dateValues: dateValues.map(d => d.paid)
},
title: 'Paid Members',
stats: stats
}
};
}
}, (error) => {
this.memberCountStatsError = error;
this.memberCountStatsLoading = false;
});
}
loadCharts() {
this.loadMRRStats();
this.loadMemberCountStats();
this.loadNewsletterOpenRates();
}
loadEvents() {
this.eventsLoading = true;
this.membersStats.fetchTimeline({limit: 5}).then(({events}) => {
this.eventsData = events;
this.eventsLoading = false;
}, (error) => {
this.eventsError = error;
this.eventsLoading = false;
});
}
loadNewsletterOpenRates() {
this.newsletterOpenRatesLoading = true;
this.membersStats.fetchNewsletterStats().then((results) => {
const rangeStartOpenRate = results.length > 1 ? results[results.length - 2].openRate : 0;
const rangeEndOpenRate = results.length > 0 ? results[results.length - 1].openRate : 0;
const percentGrowth = rangeStartOpenRate !== 0 ? ((rangeEndOpenRate - rangeStartOpenRate) / rangeStartOpenRate) * 100 : 0;
this.newsletterOpenRatesData = {
percentGrowth: percentGrowth.toFixed(1),
percentClass: (percentGrowth > 0 ? 'positive' : (percentGrowth < 0 ? 'negative' : '')),
current: rangeEndOpenRate,
options: {
rangeInDays: 30
},
data: {
label: 'Open rate',
dateLabels: results.map(d => d.subject),
dateValues: results.map(d => d.openRate)
},
title: 'Open rate',
stats: results
};
this.newsletterOpenRatesLoading = false;
}, (error) => {
this.newsletterOpenRatesError = error;
this.newsletterOpenRatesLoading = false;
});
}
loadTopMembers() {
this.topMembersLoading = true;
let query = {
filter: 'email_open_rate:-null',
order: 'email_open_rate desc',
limit: 5
};
this.store.query('member', query).then((result) => {
if (!result.length) {
return this.store.query('member', {
filter: 'status:paid',
order: 'created_at asc',
limit: 5
});
}
return result;
}).then((result) => {
this.topMembersData = result;
this.topMembersLoading = false;
}).catch((error) => {
this.topMembersError = error;
this.topMembersLoading = false;
});
}
loadWhatsNew() {
this.whatsNewEntriesLoading = true;
this.whatsNew.fetchLatest.perform().then(() => {
this.whatsNewEntriesLoading = false;
this.whatsNewEntries = this.whatsNew.entries.slice(0, 3);
}, (error) => {
this.whatsNewEntriesError = error;
this.whatsNewEntriesLoading = false;
});
}
@action
dismissLaunchBanner() {
this.settings.set('editorIsLaunchComplete', true);
this.settings.save();
}
}