-
Notifications
You must be signed in to change notification settings - Fork 110
/
index.js
151 lines (149 loc) · 6.17 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import moment from 'moment/moment';
import Chart from 'chart.js';
$(() => {
const usageFormSelector = '.usage_index';
const apiToken = $(usageFormSelector).find('input[name="api_token"]').val();
// Builds an object whose keys are the topic fro the select options and value its the value
// associated to the attribute data-url of each option
const topicToURL = $(`${usageFormSelector} select[name="topic"]`).find('option').map((i, el) => {
const topic = $(el);
return { [topic.val()]: topic.attr('data-url') };
}).get() // An array of objects { topic: URL }
.reduce((acc, value) => $.extend(acc, value), {});
const rangeDatesUpToLastYearFromNow = () => {
const getLastMonth = () => moment().subtract(1, 'month').clone();
const rangeDates = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].reduce((acc, v, i) => {
const id = getLastMonth().subtract(i, 'month').format('MMM-YY');
acc[id] = {
start_date: getLastMonth().startOf('month').subtract(i, 'month').format('YYYY-MM-DD'),
end_date: getLastMonth().endOf('month').subtract(i, 'month').format('YYYY-MM-DD'),
id,
};
return acc;
}, {});
return rangeDates;
};
const createChart = ({ selector, data, appendTolabel = '' } = {}) => {
new Chart($(selector), { // eslint-disable-line no-new
type: 'bar',
data: {
labels: Object.keys(data),
datasets: [{
data: Object.keys(data).map(k => data[k]),
backgroundColor: '#4F5253',
// TODO parameterised according to roadmap main colour instance
}],
},
options: {
legend: {
display: false,
},
tooltips: {
callbacks: {
label: tooltipItem => `${tooltipItem.yLabel} ${appendTolabel}`,
},
},
scales: {
yAxes: [{
ticks: { min: 0, suggestedMax: 50 },
}],
},
},
});
};
/*
Submit event associated to the filter by dates form
*/
$(usageFormSelector).on('submit', (e) => {
e.preventDefault();
const target = $(e.target);
const topic = target.find('select[name="topic"]').val();
const orgId = target.find('select[name="org_id"]').val() || target.find('input[name="org_id"]').val();
$('[data-topics]').hide(); // Hides data-topics container
$('[data-topic]').hide(); // Hides any data-topic specific
const ajaxSettings = ({ totals = false } = {}) => ({
headers: { Authorization: `Token token="${apiToken}"` },
url: topicToURL[topic],
data: totals ? { topic, org_id: orgId } : target.serialize(),
});
// Awaits until both AJAX request responds.
// Note, the success handler is only executed if both AJAX requests return success
$.when($.ajax(ajaxSettings()), $.ajax(ajaxSettings({ totals: true }))).then(
(dataRangeSuccessCb, dataTotalsSuccessCb) => {
let dataRange = null;
let dataTotals = null;
if (dataRangeSuccessCb[0]) { // data is the first argument of the successCb ranges
const dataKeys = Object.keys(dataRangeSuccessCb[0]);
// We assume the dataRange is the first key of the object responded
dataRange = dataKeys.length > 0 ? dataRangeSuccessCb[0][dataKeys[0]] : null;
}
if (dataTotalsSuccessCb[0]) { // data is the first argument of the successCb for totals
const dataKeys = Object.keys(dataTotalsSuccessCb[0]);
// We assume the dataTotals is the first key of the object responded
dataTotals = dataKeys.length > 0 ? dataTotalsSuccessCb[0][dataKeys[0]] : null;
}
const dataTopics = $('[data-topics]');
const views = $(`[data-topic="${topic}"]`);
dataRange !== null ? dataTopics.find('[data-range]').html(dataRange) : undefined; // eslint-disable-line no-unused-expressions
dataTotals !== null ? dataTopics.find('[data-totals]').html(dataTotals) : undefined; // eslint-disable-line no-unused-expressions
views.show();
dataTopics.show();
},
); // TODO request error handling
});
/*
Click event associated to each Export button
*/
$('button[data-url]').on('click', (e) => {
const rangeDates = rangeDatesUpToLastYearFromNow();
$.ajax({
headers: { Authorization: `Token token="${apiToken}"` },
url: $(e.currentTarget).attr('data-url'),
data: { range_dates: rangeDates },
}).then((data, statusText, jqXHR) => {
/* eslint-env browser */
const blob = new Blob([data], { type: 'text/csv' });
// Attemps to match the filename from the Content-Disposition header produced by the API
const match = /filename="([^"]*)"/.exec(jqXHR.getResponseHeader('Content-Disposition'));
const link = $('<a />', {
href: URL.createObjectURL(blob),
download: match ? match[1] : 'export.csv',
});
$('body').append(link);
link[0].click();
link.remove();
});
});
const yearlySuccesHandler = ({ data, selector } = {}) => {
const keys = Object.keys(data); // Keys are Month-Year strings and values might be [0...N]
if (keys.find(k => data[k] > 0)) {
createChart({ selector, data });
} else {
$(selector).prev().show();
}
};
// Sends an AJAX request to our two current endpoints that generate yearly data
// (e.g. users_joined_api_v0_statistics_path, created_plans_api_v0_statistics_path )
// and draws a barChart when success response is found
const initialise = () => {
// Only fire AJAX requests if topicToURL object has keys, i.e. topics mapping to URLs
if (Object.keys(topicToURL).length > 0) {
const rangeDates = rangeDatesUpToLastYearFromNow();
$.ajax({
headers: { Authorization: `Token token="${apiToken}"` },
url: topicToURL.users,
data: { range_dates: rangeDates },
}).then((data) => {
yearlySuccesHandler({ data, selector: '#yearly_users' });
}); // TODO request error handling
$.ajax({
headers: { Authorization: `Token token="${apiToken}"` },
url: topicToURL.plans,
data: { range_dates: rangeDates },
}).then((data) => {
yearlySuccesHandler({ data, selector: '#yearly_plans' });
}); // TODO request error handling
}
};
initialise();
});