Skip to content

Commit

Permalink
Merge pull request #69 from zarathustra323/story-reports
Browse files Browse the repository at this point in the history
Story reports + updated campaign reports
  • Loading branch information
brandonbk authored Aug 22, 2018
2 parents 7630a5b + c0411a2 commit 462a0fc
Show file tree
Hide file tree
Showing 63 changed files with 1,528 additions and 349 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ module.exports = {
env: {
browser: true
},
globals: {
Highcharts: true,
},
rules: {
},
overrides: [
Expand Down
23 changes: 23 additions & 0 deletions app/components/-report/campaign/chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { inject } from '@ember/service';
import moment from 'moment'

export default Component.extend({
metrics: inject(),

isLoading: false,

metricKey: 'ctr',
metricOptions: computed.reads('metrics.campaign.array').readOnly(),
selectedMetric: computed('metricKey', function() {
return this.get(`metrics.campaign.${this.get('metricKey')}`);
}).readOnly(),

startDate: computed('endDate', function() {
return moment(this.get('endDate')).subtract(14, 'days');
}),
endDate: computed(function() {
return moment();
}),
});
48 changes: 48 additions & 0 deletions app/components/-report/campaign/creative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Component from '@ember/component';
import ObjectQueryManager from 'ember-apollo-client/mixins/object-query-manager';

import reportByDay from 'fortnight/gql/queries/campaign/reports/creative-by-day';
import creativeMetrics from 'fortnight/gql/queries/campaign/creative-metrics';

export default Component.extend(ObjectQueryManager, {
isReportRunning: false,
areMetricsLoading: false,

campaignId: null,
creativeId: null,

actions: {
async runByDayReport({ startDate, endDate }) {
this.set('isReportRunning', true);
const variables = {
input: { campaignId: this.get('campaignId'), creativeId: this.get('creativeId') },
startDate: startDate.startOf('day').valueOf(),
endDate: endDate.startOf('day').valueOf(),
};
try {
const { reports } = await this.get('apollo').query({ query: reportByDay, variables }, 'campaignCreative');
this.set('rows', reports.byDay);
} catch (e) {
this.get('graphErrors').show(e);
} finally {
this.set('isReportRunning', false);
}
},

async retrieveCreativeMetrics() {
this.set('areMetricsLoading', true);
const variables = {
input: { campaignId: this.get('campaignId'), creativeId: this.get('creativeId') },
};
try {
const { metrics } = await this.get('apollo').query({ query: creativeMetrics, variables }, 'campaignCreative');
this.set('metrics', metrics);
} catch (e) {
this.get('graphErrors').show(e);
} finally {
this.set('areMetricsLoading', false);
}
},
},

});
10 changes: 10 additions & 0 deletions app/components/-report/campaign/metric-cards.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Component from '@ember/component';

export default Component.extend({
classNames: ['row'],
views: 0,
clicks: 0,
ctr: 0,

isLoading: false,
});
10 changes: 10 additions & 0 deletions app/components/-report/campaign/metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Component from '@ember/component';
import ActionMixin from 'fortnight/mixins/action';

export default Component.extend(ActionMixin, {
isLoading: false,

didInsertElement() {
this.sendEventAction('oninsert', this);
},
});
1 change: 1 addition & 0 deletions app/components/-report/data-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import Component from '@ember/component';

export default Component.extend({
classNames: ['card', 'border-0', 'z-depth-half'],
isLoading: false,
});
6 changes: 6 additions & 0 deletions app/components/-report/data-chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Component from '@ember/component';

export default Component.extend({
classNames: ['card', 'border-0', 'z-depth-half'],
isLoading: false,
});
107 changes: 107 additions & 0 deletions app/components/-report/metrics-chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import ActionMixin from 'fortnight/mixins/action';

export default Component.extend(ActionMixin, {
classNames: ['card'],

/**
* The chart start date.
*
* @type {Date}
*/
startDate: null,

/**
* The chart end date.
*
* @type {Date}
*/
endDate: null,

/**
* The currently selected metric key, e.g. `views`.
*
* @type {string}
*/
metricKey: '',

/**
* An array of metric option objects.
* For example:
* `[ { key: 'views', label: 'Impressions', tooltipFormat: '0,0', labelFormat: '0.[0]a' } ]`
*
* @type {object[]}
*/
metricOptions: null,

/**
* Determines the selected metric option object, based on the `metricKey` value.
*
* @type {object}
*/
selectedMetric: computed('metricKey', '[email protected]', function() {
return this.get('metricOptions').find(option => option.key === this.get('metricKey'));
}),

/**
* Whether data for the chart is being loaded.
*
* @type {boolean}
*/
isLoading: false,

/**
* The report data rows.
*
* Expects an object with `shortDate`, `longDate`, and `metrics`.
* For example:
* ```
* [
* { shortDate: 'Aug 14', longDate: 'Tuesday, August 14th, 2018', metrics: { views: 24 } },
* { shortDate: 'Aug 15', longDate: 'Wednesday, August 15th, 2018', metrics: { views: 12 } },
* ]
* ```
*/
rows: null,

days: computed.map('rows.@each.{shortDate,longDate}', function({ shortDate, longDate }) {
return { shortDate, longDate }
}),

data: computed('rows.[]', function() {
const key = this.get('metricKey');
const rows = this.get('rows') || [];
return rows.map(row => row.metrics[key]);
}),

didInsertElement() {
this.sendEventAction('oninsert', this);
},

/**
* Dispatches the change event.
* Will send the `startDate`, `endDate`, and `selectedMetric` as an object
* as the first argument, and the component instance as the second.
*/
dispatchChange() {
const {
startDate,
endDate,
selectedMetric,
} = this.getProperties('startDate', 'endDate', 'selectedMetric');
this.sendEventAction('onchange', { startDate, endDate, selectedMetric }, this);
},

actions: {
setMetric({ key }) {
this.set('metricKey', key);
this.dispatchChange();
},
setDates({ start, end }) {
this.set('startDate', start);
this.set('endDate', end);
this.dispatchChange();
},
},
});
132 changes: 132 additions & 0 deletions app/components/-report/metrics-chart/chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import Component from '@ember/component';
import { computed, observer } from '@ember/object';
import numeral from 'numeral';

export default Component.extend({
/**
* An array of day objects, with short and long date strings.
* The `shortDate` value will be used as the x-axis categories and
* the `longDate` value will be used for the point tooltip.
*
* For example:
* `[{ shortDate: 'Jan 1, 2018', longDate: 'Wednesday, Janaury 1st, 2018 }]`
*/
days: null,

/**
* The categories to display.
* Will be computed from the `days.[].shortDate` value.
*
* @type {array}
*/
categories: computed.mapBy('days', 'shortDate'),

/**
* The data to display.
*
* @type {array}
*/
data: null,

/**
* The y-axis label/title and series name.
*
* @type {string}
*/
label: 'Metric Label',

/**
* Whether the chart is loading.
*
* @type {boolean}
*/
isLoading: false,

/**
* The numeral tooltip format, e.g. 0,0
*/
tooltipFormat: null,

/**
* The numeral y-axis label format, e.g. 0.[0]a
*/
labelFormat: null,

series: computed(function() {
const data = this.get('data') || [];
const name = this.get('label');
return [{
name,
data,
}];
}),

extremes: computed('data.length', function() {
const length = this.get('data.length');
if (length > 1) return { min: 0.5, max: length - 1.5 };
return {};
}),

options: computed(function() {
const component = this;
return {
chart: { type: 'areaspline' },
legend: { enabled: false },
title: { text: false },
xAxis: {
categories: this.get('categories'),
min: this.get('extremes.min'),
max: this.get('extremes.max'),
minRange: 1,
},
tooltip: {
formatter: function() {
const format = component.get('tooltipFormat');
const value = format ? numeral(this.y).format(format) : this.y;
const { index, color } = this.point;
const longDate = component.get(`days.${index}.longDate`);
return `<strong>${longDate}</string><br/>
<span style="color:${color}">\u25CF</span> ${this.series.name}: <b>${value}</b>
`;
},
},
yAxis: {
title: { text: this.get('label') },
labels: {
formatter: function() {
const format = component.get('labelFormat');
return format ? numeral(this.value).format(format) : this.value;
},
},
},
plotOptions: {
areaspline: { fillOpacity: 0.5 },
},
};
}),

config: computed(function() {
const config = this.get('options') || {};
config.series = this.get('series') || [];
return config;
}),

updateChart: observer('data.[]', function() {
const chart = this.get('chart');
// Update the yAxis label.
chart.yAxis[0].setTitle({ text: this.get('label') }, false);
// Set the new xAxis categories and extremes.
chart.xAxis[0].setCategories(this.get('categories'), false);
chart.xAxis[0].setExtremes(this.get('extremes.min'), this.get('extremes.max'), false);
// Set the new series data and name.
chart.series[0].setData(this.get('data'), false);
chart.series[0].update({ name: this.get('label') }, false);
chart.redraw(true);
}),

didInsertElement() {
const config = this.get('config');
const chart = Highcharts.chart(this.$('.chart')[0], config);
this.set('chart', chart);
},
});
Loading

0 comments on commit 462a0fc

Please sign in to comment.