PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.10.1
Independent Analytics – WordPress Analytics Plugin v2.10.1
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / javascript-source / controllers / report_controller.js
independent-analytics / javascript-source / controllers Last commit date
campaign_builder_controller.js 1 year ago chart_controller.js 1 year ago chart_geo_controller.js 2 years ago chart_interval_controller.js 2 years ago clipboard_controller.js 1 year ago copy_report_controller.js 2 years ago create_report_controller.js 2 years ago delete_data_controller.js 2 years ago delete_report_controller.js 2 years ago easepick_controller.js 2 years ago export_reports_controller.js 2 years ago filters_controller.js 1 year ago group_controller.js 2 years ago import_reports_controller.js 2 years ago migration_redirect_controller.js 2 years ago modal_controller.js 2 years ago plugin_group_options_controller.js 1 year ago pruner_controller.js 1 year ago quick_stats_controller.js 2 years ago real_time_controller.js 1 year ago rename_report_controller.js 2 years ago report_controller.js 1 year ago reset_analytics_controller.js 2 years ago save_report_controller.js 2 years ago select_input_controller.js 2 years ago set_favorite_report_controller.js 2 years ago sort_controller.js 2 years ago sortable_reports_controller.js 2 years ago table_columns_controller.js 2 years ago woocommerce_settings_controller.js 1 year ago
report_controller.js
463 lines
1 import {Controller} from "@hotwired/stimulus"
2 import {downloadCSV} from '../download'
3 import html2pdf from 'html2pdf.js';
4
5 class Report_Controller extends Controller {
6 /**
7 * Store the jQuery request so that it can be canceled if a new report is requested before the
8 * current one is fetched.
9 */
10 static request;
11 static targets = ['loadMore', 'exportReportTable', 'exportReportStatistics', 'exportPDF', 'spinner']
12 static values = {
13 name: String,
14 relativeRangeId: String,
15 exactStart: String,
16 exactEnd: String,
17 group: String,
18 chartInterval: String,
19 sortColumn: String,
20 sortDirection: String,
21 columns: Array,
22 quickStats: Array,
23 primaryChartMetricId: String,
24 secondaryChartMetricId: String,
25 filters: Array
26 }
27
28 exactStart = undefined
29 exactEnd = undefined
30 relativeRangeId = undefined
31 columns = undefined
32 quickStats = undefined
33 filters = []
34 sortColumn = undefined
35 sortDirection = undefined
36 group = undefined
37 chartInterval = undefined
38 primaryChartMetricId = undefined
39 secondaryChartMetricId = undefined
40 page = 1
41
42 connect() {
43 // If any values are empty strings, set them equal to undefined instead
44 this.exactStart = this.exactStartValue === "" ? undefined : this.exactStartValue;
45 this.exactEnd = this.exactEndValue === "" ? undefined : this.exactEndValue;
46 this.relativeRangeId = this.relativeRangeIdValue === "" ? undefined : this.relativeRangeIdValue
47 this.columns = this.columnsValue
48 this.quickStats = this.quickStatsValue
49 this.group = this.groupValue
50 this.chartInterval = this.chartIntervalValue
51 this.sortColumn = this.sortColumnValue
52 this.sortDirection = this.sortDirectionValue
53 this.primaryChartMetricId = this.primaryChartMetricIdValue
54 this.secondaryChartMetricId = this.secondaryChartMetricIdValue
55 this.filters = this.filtersValue
56 document.addEventListener('iawp:changeDates', this.datesChanged)
57 document.addEventListener('iawp:changeColumns', this.columnsChanged)
58 document.addEventListener('iawp:changeQuickStats', this.quickStatsChanged)
59 document.addEventListener('iawp:changeFilters', this.filtersChanged)
60 document.addEventListener('iawp:changeSort', this.sortChanged)
61 document.addEventListener('iawp:changeGroup', this.changeGroup)
62 document.addEventListener('iawp:changeChartInterval', this.changeChartInterval)
63 document.addEventListener('iawp:changePrimaryChartMetric', this.changePrimaryChartMetric)
64 document.addEventListener('iawp:changeSecondaryChartMetric', this.changeSecondaryChartMetric)
65 document.addEventListener('iawp:fetchingReport', this.onFetchingReport)
66 setTimeout(() => {
67 this.fetch({
68 isInitialFetch: true,
69 showLoadingOverlay: false
70 })
71 }, 0)
72 }
73
74 disconnect() {
75 document.removeEventListener('iawp:changeDates', this.datesChanged)
76 document.removeEventListener('iawp:changeColumns', this.columnsChanged)
77 document.removeEventListener('iawp:changeQuickStats', this.quickStatsChanged)
78 document.removeEventListener('iawp:changeFilters', this.filtersChanged)
79 document.removeEventListener('iawp:changeSort', this.sortChanged)
80 document.removeEventListener('iawp:changeGroup', this.changeGroup)
81 document.removeEventListener('iawp:changeChartInterval', this.changeChartInterval)
82 document.removeEventListener('iawp:changePrimaryChartMetric', this.changePrimaryChartMetric)
83 document.removeEventListener('iawp:changeSecondaryChartMetric', this.changeSecondaryChartMetric)
84 document.removeEventListener('iawp:fetchingReport', this.onFetchingReport)
85 }
86
87 emitChangedOption(detail) {
88 document.dispatchEvent(
89 new CustomEvent('iawp:changedOption', {
90 detail
91 })
92 )
93 }
94
95 changePrimaryChartMetric = (e) => {
96 this.primaryChartMetricId = e.detail.primaryChartMetricId
97 this.emitChangedOption({
98 'primary_chart_metric_id': this.primaryChartMetricId
99 })
100 }
101
102 changeSecondaryChartMetric = (e) => {
103 this.secondaryChartMetricId = e.detail.secondaryChartMetricId
104 this.emitChangedOption({
105 'secondary_chart_metric_id': this.secondaryChartMetricId
106 })
107 }
108
109 datesChanged = (e) => {
110 this.exactStart = e.detail.exactStart
111 this.exactEnd = e.detail.exactEnd
112 this.relativeRangeId = e.detail.relativeRangeId
113 this.page = 1;
114
115 this.emitChangedOption({
116 'exact_start': this.exactStart || null,
117 'exact_end': this.exactEnd || null,
118 'relative_range_id': this.relativeRangeId || null
119 })
120 this.fetch({newDateRange: true})
121 }
122
123 columnsChanged = (e) => {
124 this.columns = e.detail.optionIds
125 this.emitChangedOption({
126 'columns': this.columns
127 })
128 }
129
130 quickStatsChanged = (e) => {
131 this.quickStats = e.detail.optionIds
132 this.emitChangedOption({
133 'quick_stats': this.quickStats
134 })
135 }
136
137 filtersChanged = (e) => {
138 this.filters = e.detail.filters
139 this.page = 1;
140
141 this.emitChangedOption({
142 'filters': this.filters
143 })
144 this.fetch({showLoadingOverlay: e.detail.showLoadingOverlay})
145 }
146
147 sortChanged = (e) => {
148 this.sortColumn = e.detail.sortColumn
149 this.sortDirection = e.detail.sortDirection
150 this.page = 1;
151
152 this.emitChangedOption({
153 'sort_column': this.sortColumn,
154 'sort_direction': this.sortDirection
155 })
156 this.fetch()
157 }
158
159 changeGroup = (e) => {
160 if (this.group === e.detail.group) {
161 return;
162 }
163
164 this.group = e.detail.group
165 this.page = 1
166 this.emitChangedOption({
167 'group_name': this.group
168 })
169 this.fetch({newGroup: true});
170 }
171
172 changeChartInterval = (e) => {
173 const chartInterval = e.detail.chartInterval
174
175 if (this.chartInterval === chartInterval) {
176 return;
177 }
178
179 this.chartInterval = chartInterval
180 this.emitChangedOption({
181 'chart_interval': this.chartInterval
182 })
183 this.fetch();
184 }
185
186 onFetchingReport = () => {
187 this.spinnerTarget.classList.remove('hidden')
188
189 document.addEventListener('iawp:fetchedReport', () => {
190 this.spinnerTarget.classList.add('hidden')
191 }, {once: true})
192 }
193
194 loadMore = () => {
195 this.page = this.page + 1
196
197 this.fetch()
198 }
199
200 fetch({
201 isInitialFetch = false,
202 showLoadingOverlay = true,
203 newGroup = false,
204 newDateRange = false
205 } = {}) {
206
207 if (showLoadingOverlay) {
208 jQuery('#iawp-parent').addClass('loading');
209 }
210
211 const data = {
212 ...iawpActions.filter,
213 'filters': this.filters,
214 'exact_start': this.exactStart,
215 'exact_end': this.exactEnd,
216 'is_new_date_range': newDateRange,
217 'relative_range_id': this.relativeRangeId,
218 'table_type': jQuery('#data-table').data('table-name'),
219 'columns': this.columns,
220 'report_quick_stats': ['visitors'], // TODO
221 'primary_chart_metric_id': this.primaryChartMetricId,
222 'secondary_chart_metric_id': this.secondaryChartMetricId,
223 'sort_column': this.sortColumn,
224 'quick_stats': this.quickStats,
225 'sort_direction': this.sortDirection,
226 'group': this.group,
227 'is_new_group': newGroup,
228 'chart_interval': this.chartInterval,
229 'page': this.page,
230 };
231
232 if (Report_Controller.request) {
233 Report_Controller.request.abort();
234 }
235
236 document.dispatchEvent(
237 new CustomEvent('iawp:fetchingReport')
238 )
239
240 Report_Controller.request = jQuery.post(ajaxurl, data, (response) => {
241 response = response.data
242
243 this.columns = response.columns
244 this.filters = response.filters
245 this.chartInterval = response.chartInterval
246
247 jQuery('#iawp-columns .row-number').text(response.totalNumberOfRows.toLocaleString());
248 document.getElementById('data-table').setAttribute('data-total-number-of-rows', response.totalNumberOfRows)
249 document.dispatchEvent(
250 new CustomEvent('iawp:fetchedReport')
251 )
252
253 if (newGroup) {
254 jQuery('#iawp-table-wrapper').replaceWith(response.table)
255 jQuery('[data-plugin-group-options-option-type-value=columns]').replaceWith(response.columnsHTML)
256
257 document.dispatchEvent(
258 new CustomEvent('iawp:changeColumns', {
259 detail: {
260 optionIds: this.columns
261 }
262 })
263 )
264 } else {
265 jQuery('#iawp-rows').replaceWith(response.rows);
266 }
267
268 jQuery('#dates-button span:last-child').text(response.label)
269
270 const parser = new DOMParser();
271 const statsDocument = parser.parseFromString(response.stats, 'text/html');
272 jQuery('#quick-stats .iawp-stats').replaceWith(statsDocument.querySelector('.iawp-stats'))
273 jQuery('#quick-stats').removeClass('skeleton-ui');
274
275 if (isInitialFetch && this.filtersValue.length === 0) {
276 // Do not update the chart if there are no filters and it's the initial load
277 } else {
278 jQuery('#independent-analytics-chart').closest('.chart-container').replaceWith(response.chart);
279 }
280
281 document.dispatchEvent(
282 new CustomEvent('iawp:updateColumnsUserInterface')
283 )
284
285 document.dispatchEvent(
286 new CustomEvent('iawp:groupChanged', {
287 detail: {
288 groupId: response.groupId
289 }
290 })
291 )
292
293 document.dispatchEvent(
294 new CustomEvent('iawp:filtersChanged', {
295 detail: {
296 filtersTemplateHTML: response.filtersTemplateHTML,
297 filtersButtonsHTML: response.filtersButtonsHTML,
298 filters: response.filters
299 }
300 })
301 )
302
303 this.exportPDFTarget.removeAttribute('disabled')
304
305 if (response.isLastPage) {
306 this.loadMoreTarget.setAttribute('disabled', 'disabled')
307 } else {
308 this.loadMoreTarget.removeAttribute('disabled')
309 }
310
311 jQuery('#iawp-parent').removeClass('loading');
312 });
313 }
314
315 exportReportTable() {
316 const data = {
317 ...iawpActions.export_report_table,
318 'table_type': jQuery('#data-table').data('table-name'),
319 'columns': this.columns,
320 'filters': this.filters,
321 'exact_start': this.exactStart,
322 'exact_end': this.exactEnd,
323 'relative_range_id': this.relativeRangeId,
324 'sort_column': this.sortColumn,
325 'sort_direction': this.sortDirection,
326 'group': this.group,
327 };
328
329 this.exportReportTableTarget.classList.add('sending')
330 this.exportReportTableTarget.setAttribute('disabled', 'disabled')
331
332 if (Report_Controller.csvRequest) {
333 Report_Controller.csvRequest.abort();
334 }
335
336 Report_Controller.csvRequest = jQuery.post(ajaxurl, data, (response) => {
337 downloadCSV(this.getFileName('csv', 'table'), response.data.csv)
338 this.exportReportTableTarget.classList.add('sent')
339 this.exportReportTableTarget.classList.remove('sending')
340 this.exportReportTableTarget.removeAttribute('disabled')
341
342 setTimeout(() => {
343 this.exportReportTableTarget.classList.remove('sent')
344 }, 1000)
345 })
346 }
347
348 exportReportStatistics() {
349 const data = {
350 ...iawpActions.export_report_statistics,
351 'filters': this.filters,
352 'exact_start': this.exactStart,
353 'exact_end': this.exactEnd,
354 'is_new_date_range': false,
355 'relative_range_id': this.relativeRangeId,
356 'table_type': jQuery('#data-table').data('table-name'),
357 'columns': this.columns,
358 'sort_column': this.sortColumn,
359 'quick_stats': this.quickStats,
360 'sort_direction': this.sortDirection,
361 'group': this.group,
362 'is_new_group': false,
363 'chart_interval': this.chartInterval,
364 'page': this.page,
365 };
366
367 this.exportReportStatisticsTarget.classList.add('sending')
368 this.exportReportStatisticsTarget.setAttribute('disabled', 'disabled')
369
370 if (Report_Controller.csvRequest) {
371 Report_Controller.csvRequest.abort();
372 }
373
374 Report_Controller.csvRequest = jQuery.post(ajaxurl, data, (response) => {
375 downloadCSV(this.getFileName('csv', 'statistics'), response.data.csv)
376 this.exportReportStatisticsTarget.classList.add('sent')
377 this.exportReportStatisticsTarget.classList.remove('sending')
378 this.exportReportStatisticsTarget.removeAttribute('disabled')
379
380 setTimeout(() => {
381 this.exportReportStatisticsTarget.classList.remove('sent')
382 }, 1000)
383 })
384 }
385
386 exportPDF() {
387 this.exportPDFTarget.classList.add('sending')
388 this.exportPDFTarget.setAttribute('disabled', 'disabled')
389
390 setTimeout(() => {
391 const element = document.getElementById('wpwrap').cloneNode(true)
392 const options = {
393 filename: this.getFileName('pdf'),
394 jsPDF: {
395 unit: 'in',
396 format: 'letter',
397 orientation: 'landscape'
398 },
399 }
400 let base64Image = null
401
402 if (window.iawp_chart) {
403 const chartImageWidth = 1080
404 const chartImageHeight = chartImageWidth * .25
405 window.iawp_chart.resize(chartImageWidth, chartImageHeight)
406 base64Image = window.iawp_chart.toBase64Image('image/png', 1);
407 window.iawp_chart.resize()
408 }
409
410 if (window.iawp_geo_chart) {
411 base64Image = window.iawp_geo_chart.getImageURI()
412 }
413
414 if (base64Image) {
415 const imageElement = document.createElement('img')
416 imageElement.src = base64Image
417
418 element.querySelector('#independent-analytics-chart').replaceWith(imageElement)
419 }
420
421 // Prevent stimulus controllers from firing
422 element.querySelectorAll('[data-controller]').forEach((element) => {
423 element.removeAttribute('data-controller')
424 })
425
426 // Preserve selected values in clone by manually adding "selected" to options
427 element.querySelectorAll('select').forEach((element) => {
428 if (!element.id) {
429 return
430 }
431
432 const originalValue = document.getElementById(element.id).value
433
434 element.options.forEach((option) => {
435 option.toggleAttribute('selected', option.value === originalValue)
436 })
437 })
438
439 html2pdf().set(options).from(element).toContainer().save().then(() => {
440 this.exportPDFTarget.classList.add('sent')
441 this.exportPDFTarget.classList.remove('sending')
442 this.exportPDFTarget.removeAttribute('disabled')
443
444 setTimeout(() => {
445 this.exportPDFTarget.classList.remove('sent')
446 }, 1000)
447 })
448 }, 250) // Allow animations to finish before exporting blocks things up
449 }
450
451 getFileName(fileExtension, type = null) {
452 const reportTitleElement = document.querySelector('#report-title-bar .report-title')
453 let reportTitle = reportTitleElement ? reportTitleElement.innerText : 'report'
454
455 if (type) {
456 reportTitle += '-' + type
457 }
458
459 return reportTitle.replace(/[^a-zA-Z0-9]+/g, '-').toLowerCase() + '.' + fileExtension
460 }
461 }
462
463 export default Report_Controller;