mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-01 07:16:52 +03:00
Implemented referrers stats API on posts' analytics page
refs https://github.com/TryGhost/Team/issues/1921
This commit is contained in:
parent
7437d92d50
commit
7cadaa6378
@ -38,13 +38,13 @@ export default class SourceAttributionChart extends Component {
|
|||||||
if (this.chartType === 'free') {
|
if (this.chartType === 'free') {
|
||||||
const sortedByFree = [...this.sources];
|
const sortedByFree = [...this.sources];
|
||||||
sortedByFree.sort((a, b) => {
|
sortedByFree.sort((a, b) => {
|
||||||
return b.freeSignups - a.freeSignups;
|
return b.signups - a.signups;
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
labels: sortedByFree.slice(0, 5).map(source => source.source),
|
labels: sortedByFree.slice(0, 5).map(source => source.source),
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Free Signups',
|
label: 'Free Signups',
|
||||||
data: sortedByFree.slice(0, 5).map(source => source.freeSignups),
|
data: sortedByFree.slice(0, 5).map(source => source.signups),
|
||||||
backgroundColor: CHART_COLORS.slice(0, 5),
|
backgroundColor: CHART_COLORS.slice(0, 5),
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderColor: '#fff'
|
borderColor: '#fff'
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="gh-dashboard-list-item-sub">
|
<div class="gh-dashboard-list-item-sub">
|
||||||
<span class="gh-dashboard-metric-minivalue">
|
<span class="gh-dashboard-metric-minivalue">
|
||||||
{{#if sourceData.freeSignups}}
|
{{#if sourceData.signups}}
|
||||||
{{format-number sourceData.freeSignups}}
|
{{format-number sourceData.signups}}
|
||||||
{{else}}
|
{{else}}
|
||||||
—
|
—
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -1,34 +1,45 @@
|
|||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
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
|
* @typedef {import('../../services/dashboard-stats').SourceAttributionCount} SourceAttributionCount
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class AnalyticsController extends Controller {
|
export default class AnalyticsController extends Controller {
|
||||||
|
@service ajax;
|
||||||
|
@service ghostPaths;
|
||||||
|
|
||||||
|
@tracked sources = null;
|
||||||
|
|
||||||
get post() {
|
get post() {
|
||||||
return this.model;
|
return this.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@action
|
||||||
* @returns {SourceAttributionCount[]} - array of objects with source and count properties
|
loadData() {
|
||||||
*/
|
this.fetchReferrersStats();
|
||||||
get sources() {
|
}
|
||||||
return [
|
|
||||||
{
|
async fetchReferrersStats() {
|
||||||
source: 'Twitter',
|
if (this._fetchReferrersStats.isRunning) {
|
||||||
freeSignups: 12,
|
return this._fetchReferrersStats.last;
|
||||||
paidConversions: 50
|
}
|
||||||
},
|
return this._fetchReferrersStats.perform();
|
||||||
{
|
}
|
||||||
source: 'Google',
|
|
||||||
freeSignups: 9,
|
@task
|
||||||
paidConversions: 32
|
*_fetchReferrersStats() {
|
||||||
},
|
let statsUrl = this.ghostPaths.url.api(`stats/referrers/posts/${this.post.id}`);
|
||||||
{
|
let result = yield this.ajax.request(statsUrl);
|
||||||
source: 'Direct',
|
this.sources = result.stats.map((stat) => {
|
||||||
freeSignups: 2,
|
return {
|
||||||
paidConversions: 40
|
source: stat.source ?? 'Direct',
|
||||||
}
|
signups: stat.signups,
|
||||||
];
|
paidConversions: stat.paid_conversions
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,7 @@ export default class DashboardMocksService extends Service {
|
|||||||
this.memberAttributionStats.push({
|
this.memberAttributionStats.push({
|
||||||
date: date.toISOString().split('T')[0],
|
date: date.toISOString().split('T')[0],
|
||||||
source: attributionSources[Math.floor(Math.random() * attributionSources.length)],
|
source: attributionSources[Math.floor(Math.random() * attributionSources.length)],
|
||||||
freeSignups: Math.floor(Math.random() * 50),
|
signups: Math.floor(Math.random() * 50),
|
||||||
paidConversions: Math.floor(Math.random() * 30)
|
paidConversions: Math.floor(Math.random() * 30)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import {tracked} from '@glimmer/tracking';
|
|||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @property {string} date The date (YYYY-MM-DD) on which these counts were recorded
|
* @property {string} date The date (YYYY-MM-DD) on which these counts were recorded
|
||||||
* @property {number} source Attribution Source
|
* @property {number} source Attribution Source
|
||||||
* @property {number} freeSignups Total free members signed up for this source
|
* @property {number} signups Total free members signed up for this source
|
||||||
* @property {number} paidConversions Total paid conversions for this source
|
* @property {number} paidConversions Total paid conversions for this source
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ import {tracked} from '@glimmer/tracking';
|
|||||||
* @typedef SourceAttributionCount
|
* @typedef SourceAttributionCount
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @property {string} source Attribution Source
|
* @property {string} source Attribution Source
|
||||||
* @property {number} freeSignups Total free members signed up for this source
|
* @property {number} signups Total free members signed up for this source
|
||||||
* @property {number} paidConversions Total paid conversions for this source
|
* @property {number} paidConversions Total paid conversions for this source
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -248,18 +248,18 @@ export default class DashboardStatsService extends Service {
|
|||||||
}).reduce((acc, stat) => {
|
}).reduce((acc, stat) => {
|
||||||
const existingSource = acc.find(s => s.source === stat.source);
|
const existingSource = acc.find(s => s.source === stat.source);
|
||||||
if (existingSource) {
|
if (existingSource) {
|
||||||
existingSource.freeSignups += stat.freeSignups || 0;
|
existingSource.signups += stat.signups || 0;
|
||||||
existingSource.paidConversions += stat.paidConversions || 0;
|
existingSource.paidConversions += stat.paidConversions || 0;
|
||||||
} else {
|
} else {
|
||||||
acc.push({
|
acc.push({
|
||||||
source: stat.source,
|
source: stat.source,
|
||||||
freeSignups: stat.freeSignups || 0,
|
signups: stat.signups || 0,
|
||||||
paidConversions: stat.paidConversions || 0
|
paidConversions: stat.paidConversions || 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, []).sort((a, b) => {
|
}, []).sort((a, b) => {
|
||||||
return (b.freeSignups + b.paidConversions) - (a.freeSignups - a.paidConversions);
|
return (b.signups + b.paidConversions) - (a.signups - a.paidConversions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,23 +86,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if (feature 'sourceAttribution')}}
|
{{#if (feature 'sourceAttribution')}}
|
||||||
<h4 class="gh-main-section-header small bn">
|
<h4 class="gh-main-section-header small bn" {{did-insert this.loadData}}>
|
||||||
Source attribution
|
Source attribution
|
||||||
</h4>
|
</h4>
|
||||||
<div class="gh-post-analytics-box resources">
|
{{#if this.sources}}
|
||||||
<div style="display: grid;
|
<div class="gh-post-analytics-box resources">
|
||||||
grid-template-columns: 2fr 1fr;
|
<div style="display: grid;
|
||||||
grid-gap: 64px;
|
grid-template-columns: 2fr 1fr;
|
||||||
">
|
grid-gap: 64px;
|
||||||
<MemberAttribution::SourceAttributionTable @sources={{this.sources}} />
|
">
|
||||||
|
<MemberAttribution::SourceAttributionTable @sources={{this.sources}} />
|
||||||
|
|
||||||
<div style="border-left: 1px solid #eceef0; padding-left: 48px;display: flex;justify-content: center;align-items: center;">
|
<div style="border-left: 1px solid #eceef0; padding-left: 48px;display: flex;justify-content: center;align-items: center;">
|
||||||
<div style="max-width: 200px;">
|
<div style="max-width: 200px;">
|
||||||
<MemberAttribution::SourceAttributionChart @sources={{this.sources}} />
|
<MemberAttribution::SourceAttributionChart @sources={{this.sources}} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<h4 class="gh-main-section-header small bn">
|
<h4 class="gh-main-section-header small bn">
|
||||||
|
Loading…
Reference in New Issue
Block a user