-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #69 from zarathustra323/story-reports
Story reports + updated campaign reports
- Loading branch information
Showing
63 changed files
with
1,528 additions
and
349 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,9 @@ module.exports = { | |
env: { | ||
browser: true | ||
}, | ||
globals: { | ||
Highcharts: true, | ||
}, | ||
rules: { | ||
}, | ||
overrides: [ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
}, | ||
}, | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}, | ||
}); |
Oops, something went wrong.