mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-03 03:55:26 +03:00
5a029620fd
refs b027ed6378
- analytics page was split between old and new behind the audience feedback flag
- the new page had missing method as we switched the page to new one once the flag was switched on
259 lines
7.3 KiB
JavaScript
259 lines
7.3 KiB
JavaScript
import Component from '@glimmer/component';
|
|
import {action} from '@ember/object';
|
|
import {inject as service} from '@ember/service';
|
|
import {task} from 'ember-concurrency';
|
|
import {tracked} from '@glimmer/tracking';
|
|
|
|
/**
|
|
* @typedef {import('../../services/dashboard-stats').SourceAttributionCount} SourceAttributionCount
|
|
*/
|
|
|
|
const DISPLAY_OPTIONS = [{
|
|
name: 'Free signups',
|
|
value: 'signups'
|
|
}, {
|
|
name: 'Paid conversions',
|
|
value: 'paid'
|
|
}];
|
|
|
|
export default class Analytics extends Component {
|
|
@service ajax;
|
|
@service ghostPaths;
|
|
@service settings;
|
|
@service membersUtils;
|
|
@service utils;
|
|
@service feature;
|
|
|
|
@tracked sources = null;
|
|
@tracked links = null;
|
|
@tracked sortColumn = 'signups';
|
|
@tracked showSuccess;
|
|
@tracked updateLinkId;
|
|
displayOptions = DISPLAY_OPTIONS;
|
|
|
|
get post() {
|
|
return this.args.post;
|
|
}
|
|
|
|
get allowedDisplayOptions() {
|
|
if (!this.hasPaidConversionData) {
|
|
return this.displayOptions.filter(d => d.value === 'signups');
|
|
}
|
|
|
|
if (!this.hasFreeSignups) {
|
|
return this.displayOptions.filter(d => d.value === 'paid');
|
|
}
|
|
|
|
return this.displayOptions;
|
|
}
|
|
|
|
get isDropdownDisabled() {
|
|
if (!this.hasPaidConversionData || !this.hasFreeSignups) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
get selectedDisplayOption() {
|
|
if (!this.hasPaidConversionData) {
|
|
return this.displayOptions.find(d => d.value === 'signups');
|
|
}
|
|
|
|
if (!this.hasFreeSignups) {
|
|
return this.displayOptions.find(d => d.value === 'paid');
|
|
}
|
|
|
|
return this.displayOptions.find(d => d.value === this.sortColumn) ?? this.displayOptions[0];
|
|
}
|
|
|
|
get selectedSortColumn() {
|
|
if (!this.hasPaidConversionData) {
|
|
return 'signups';
|
|
}
|
|
|
|
if (!this.hasFreeSignups) {
|
|
return 'paid';
|
|
}
|
|
return this.sortColumn;
|
|
}
|
|
|
|
get hasPaidConversionData() {
|
|
return this.sources.some(sourceData => sourceData.paidConversions > 0);
|
|
}
|
|
|
|
get hasFreeSignups() {
|
|
return this.sources.some(sourceData => sourceData.signups > 0);
|
|
}
|
|
|
|
get totalFeedback() {
|
|
return this.post.count.positive_feedback + this.post.count.negative_feedback;
|
|
}
|
|
|
|
@action
|
|
onDisplayChange(selected) {
|
|
this.sortColumn = selected.value;
|
|
}
|
|
|
|
@action
|
|
setSortColumn(column) {
|
|
this.sortColumn = column;
|
|
}
|
|
|
|
@action
|
|
updateLink(linkId, linkTo) {
|
|
if (this._updateLinks.isRunning) {
|
|
return this._updateLinks.last;
|
|
}
|
|
return this._updateLinks.perform(linkId, linkTo);
|
|
}
|
|
|
|
@action
|
|
loadData() {
|
|
if (this.showSources) {
|
|
this.fetchReferrersStats();
|
|
} else {
|
|
this.sources = [];
|
|
}
|
|
|
|
if (this.showLinks) {
|
|
this.fetchLinks();
|
|
} else {
|
|
this.links = [];
|
|
}
|
|
}
|
|
|
|
updateLinkData(linksData) {
|
|
let updatedLinks;
|
|
if (this.links?.length) {
|
|
updatedLinks = this.links.map((link) => {
|
|
let linkData = linksData.find(l => l.link.link_id === link.link.link_id);
|
|
if (linkData) {
|
|
return {
|
|
...linkData,
|
|
link: {
|
|
...linkData.link,
|
|
originalTo: linkData.link.to,
|
|
to: this.utils.cleanTrackedUrl(linkData.link.to, false),
|
|
title: this.utils.cleanTrackedUrl(linkData.link.to, true)
|
|
}
|
|
};
|
|
}
|
|
return link;
|
|
});
|
|
} else {
|
|
updatedLinks = linksData.map((link) => {
|
|
return {
|
|
...link,
|
|
link: {
|
|
...link.link,
|
|
originalTo: link.link.to,
|
|
to: this.utils.cleanTrackedUrl(link.link.to, false),
|
|
title: this.utils.cleanTrackedUrl(link.link.to, true)
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
// Remove duplicates by title ad merge
|
|
const linksByTitle = updatedLinks.reduce((acc, link) => {
|
|
if (!acc[link.link.title]) {
|
|
acc[link.link.title] = link;
|
|
} else {
|
|
acc[link.link.title].clicks += link.clicks;
|
|
}
|
|
return acc;
|
|
}, {});
|
|
|
|
this.links = Object.values(linksByTitle);
|
|
}
|
|
|
|
async fetchReferrersStats() {
|
|
if (this._fetchReferrersStats.isRunning) {
|
|
return this._fetchReferrersStats.last;
|
|
}
|
|
return this._fetchReferrersStats.perform();
|
|
}
|
|
|
|
async fetchLinks() {
|
|
if (this._fetchLinks.isRunning) {
|
|
return this._fetchLinks.last;
|
|
}
|
|
return this._fetchLinks.perform();
|
|
}
|
|
|
|
@task
|
|
*_updateLinks(linkId, newLink) {
|
|
this.updateLinkId = linkId;
|
|
let currentLink;
|
|
this.links = this.links?.map((link) => {
|
|
if (link.link.link_id === linkId) {
|
|
currentLink = new URL(link.link.originalTo);
|
|
return {
|
|
...link,
|
|
link: {
|
|
...link.link,
|
|
to: this.utils.cleanTrackedUrl(newLink, false),
|
|
title: this.utils.cleanTrackedUrl(newLink, true)
|
|
}
|
|
};
|
|
}
|
|
return link;
|
|
});
|
|
|
|
const filter = `post_id:${this.post.id}+to:'${currentLink}'`;
|
|
let bulkUpdateUrl = this.ghostPaths.url.api(`links/bulk`) + `?filter=${encodeURIComponent(filter)}`;
|
|
yield this.ajax.put(bulkUpdateUrl, {
|
|
data: {
|
|
bulk: {
|
|
action: 'updateLink',
|
|
meta: {link: {to: newLink}}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Refresh links data
|
|
const linksFilter = `post_id:${this.post.id}`;
|
|
let statsUrl = this.ghostPaths.url.api(`links/`) + `?filter=${encodeURIComponent(linksFilter)}`;
|
|
let result = yield this.ajax.request(statsUrl);
|
|
this.updateLinkData(result.links);
|
|
this.showSuccess = this.updateLinkId;
|
|
setTimeout(() => {
|
|
this.showSuccess = null;
|
|
}, 2000);
|
|
}
|
|
|
|
@task
|
|
*_fetchReferrersStats() {
|
|
let statsUrl = this.ghostPaths.url.api(`stats/referrers/posts/${this.post.id}`);
|
|
let result = yield this.ajax.request(statsUrl);
|
|
this.sources = result.stats.map((stat) => {
|
|
return {
|
|
source: stat.source ?? 'Direct',
|
|
signups: stat.signups,
|
|
paidConversions: stat.paid_conversions
|
|
};
|
|
});
|
|
}
|
|
|
|
@task
|
|
*_fetchLinks() {
|
|
const filter = `post_id:${this.post.id}`;
|
|
let statsUrl = this.ghostPaths.url.api(`links/`) + `?filter=${encodeURIComponent(filter)}`;
|
|
let result = yield this.ajax.request(statsUrl);
|
|
this.updateLinkData(result.links);
|
|
}
|
|
|
|
get showLinks() {
|
|
return this.post.showEmailClickAnalytics;
|
|
}
|
|
|
|
get showSources() {
|
|
return this.feature.get('sourceAttribution') && this.post.showAttributionAnalytics;
|
|
}
|
|
|
|
get isLoaded() {
|
|
return this.links !== null && this.souces !== null;
|
|
}
|
|
}
|