PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 1.7.0.9 All 211 releases
charitable / assets / js / admin / charitable-admin-reporting.js

charitable-admin-reporting.js in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at assets/js/admin/charitable-admin-reporting.js

1,780 lines 67.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global charitable_reporting, wpchar, wpCookies */
2 // eslint-disable-line no-unused-vars
3
4 var CharitableAdminReporting = window.CharitableAdminReporting || (function (document, window, $) {
5
6 var s = {},
7 $reports,
8 showGrowthToolsNotice = false,
9 elements = {},
10 charts = {};
11
12 var app = {
13
14 settings: {
15 clickWatch: false
16 },
17
18 /**
19 * Start the engine.
20 *
21 * @since 1.8.1
22 */
23 init: function () {
24
25 s = this.settings;
26
27 // Document ready.
28 $(app.ready);
29
30 },
31
32 /**
33 * Document ready.
34 *
35 * @since 1.8.1
36 *
37 */
38 ready: function () { // check to see if javascript has been defined.
39
40 // check to see if charitable_reporting has been defined.
41 if ( typeof charitable_reporting === 'undefined' ) {
42 return;
43 }
44
45 wpchar.debug('charitable_reporting');
46 wpchar.debug(charitable_reporting);
47
48 $reports = $('#charitable-reports');
49
50 if ( $reports.length === 0 ) {
51 return;
52 }
53
54 // UI elements.
55 elements.$start_datepicker = $( '#charitable-reports-start_date' );
56 elements.$end_datepicker = $( '#charitable-reports-end_date' );
57 elements.$filter_button = $( '#charitable-reports-filter-button');
58 elements.$report_campaign_filter = $('#report-campaign-filter');
59 elements.$report_category_filter = $('#report-category-filter');
60
61 // Data containers.
62 elements.$top_donation_amount = $('#charitable-top-donation-total-amount');
63 elements.$top_donation_count = $('#charitable-top-donation-total-count');
64 elements.$top_donation_average = $('#charitable-top-donation-average');
65 elements.$top_donation_donors_count = $('#charitable-top-donor-count');
66 elements.$top_charitable_refund_total_amount = $('#charitable-top-refund-total-amount');
67 elements.$top_charitable_refund_count = $('#charitable-top-refund-count');
68 elements.$top_donors_list = $('#charitable-top-donors-list');
69 elements.$donations_breakdown_list = $('#donations-breakdown-list');
70 elements.$payment_methods_list = $('ul#charitable-payment-methods-list');
71 elements.$activity_list = $('#charitable-activity-list');
72 elements.$top_campaigns_list = $('#charitable-top-campaigns-list');
73 elements.$report_date_range_filter = $('#report-date-range-filter');
74 elements.$topnav_datepicker = $('#charitable-reports-topnav-datepicker');
75 elements.$topnav_datepicker_comparefrom = $('#charitable-reports-topnav-datepicker-comparefrom-lybunt');
76 elements.$topnav_datepicker_compareto = $('#charitable-reports-topnav-datepicker-compareto-lybunt');
77
78 s.datePickerStartDate = '';
79 s.datePickerEndDate = '';
80
81 s.datePickerCompareFromStartDate = '';
82 s.datePickerCompareFromEndDate = '';
83 s.datePickerCompareToStartDate = '';
84 s.datePickerCompareToEndDate = '';
85
86
87 if ( app.isAdvancedPage() ) {
88 app.initDatePickerRanges( 'ranged' );
89 } else {
90 app.initDatePickerRanges( '' );
91 }
92
93 app.initDatePicker();
94
95 // Bind all actions.
96 app.bindUIActions();
97
98 app.initCharts();
99 app.renderCharts();
100
101 app.checkCategoryDropdown();
102
103 app.updateDatePickerVars();
104
105 app.initPagination();
106
107 },
108
109 /**
110 * Updates the datepicker vars.
111 *
112 * @since 1.8.1
113 *
114 */
115 updateDatePickerVars: function () {
116
117 if ( $reports.find('input.charitable-datepicker-ranged').length === 0 ) {
118 return;
119 }
120
121 var drp = $reports.find('input.charitable-datepicker-ranged').data('daterangepicker');
122
123 s.datePickerStartDate = ( drp.startDate._i ) ? drp.startDate._i : '';
124 s.datePickerEndDate = ( drp.endDate._i ) ? drp.endDate._i : '';
125
126 },
127
128 /**
129 * Checks to see if the category dropdown should be enabled on the page (overview).
130 *
131 * @since 1.8.1
132 *
133 */
134 checkCategoryDropdown: function () {
135
136 if ( elements.$report_campaign_filter.val() === '-1' ) {
137 elements.$report_category_filter.removeClass('charitable-disabled');
138 } else {
139 elements.$report_category_filter.val('-1').addClass('charitable-disabled');
140 }
141
142 },
143
144 /**
145 * Builds the "All Time" preset range - the earliest donation on the site
146 * through today.
147 *
148 * The existing presets stop at "This Year", so answering "what have we
149 * raised?" meant hand-typing a multi-year range. Selecting the range still
150 * requires pressing Filter, as every other preset does.
151 *
152 * @since 1.8.12.4
153 *
154 */
155 getAllTimeRange: function () {
156
157 var earliest = false;
158
159 if ( typeof charitable_reporting !== 'undefined' && charitable_reporting.earliest_donation_date ) { // eslint-disable-line
160 earliest = moment( charitable_reporting.earliest_donation_date, 'YYYY/MM/DD' ); // eslint-disable-line
161 }
162
163 // No localized date (or an unparseable one) - fall back to a span wide
164 // enough to cover the data rather than silently showing a short range.
165 if ( ! earliest || ! earliest.isValid() ) {
166 earliest = moment().subtract( 10, 'years' ); // eslint-disable-line
167 }
168
169 return [ earliest, moment() ]; // eslint-disable-line
170 },
171
172 /**
173 * The translated label for the "All Time" preset.
174 *
175 * bootstrap-daterangepicker uses each preset's object KEY as its visible
176 * label, so this string cannot live in the JS - it has to be localized and
177 * then used as a computed key.
178 *
179 * Falls back to the English literal when the key is absent, which is a real
180 * case rather than defensive padding: `charitable_reporting` is localized by
181 * more than one class and only Charitable_Reports supplies these keys. The
182 * Dashboard would otherwise render `undefined` as the preset's label.
183 *
184 * @since 1.8.12.4
185 *
186 */
187 getAllTimeLabel: function () {
188
189 if ( typeof charitable_reporting !== 'undefined' && charitable_reporting.all_time_label ) { // eslint-disable-line
190 return charitable_reporting.all_time_label; // eslint-disable-line
191 }
192
193 return 'All Time';
194 },
195
196 /**
197 * Inits the date picker ranges, for when that UI needs to be initialized.
198 *
199 * @since 1.8.1
200 *
201 */
202 initDatePickerRanges: function ( pickerType = 'ranged' ) {
203
204 // Determine args for datepickers on various report pages.
205 if ( pickerType === 'ranged' ) {
206
207 // if moment is defined, use it to set the datepicker ranges.
208 if ( typeof moment !== 'undefined' ) {
209
210 s.ranges = {
211 'This Year': [
212 moment().startOf('year'), moment().endOf('year') // eslint-disable-line
213 ],
214 'Last Year': [
215 moment().subtract(1, 'year').startOf('year'), // eslint-disable-line
216 moment().subtract(1, 'year').endOf('year') // eslint-disable-line
217 ],
218 'This Month': [
219 moment().startOf('month'), moment().endOf('month') // eslint-disable-line
220 ],
221 'Last Month': [
222 moment().subtract(1, 'month').startOf('month'), // eslint-disable-line
223 moment().subtract(1, 'month').endOf('month') // eslint-disable-line
224 ]
225 }
226
227 // Assigned separately because the label is the key, and it is
228 // localized. The four labels above are hardcoded English, which is
229 // this file's existing convention - they are left alone rather
230 // than converted as part of a port.
231 s.ranges[ app.getAllTimeLabel() ] = app.getAllTimeRange();
232
233 }
234
235 } else { // default to overview page.
236
237 if ( typeof moment !== 'undefined' ) {
238
239 s.ranges = {
240 'Today': [
241 moment(), moment() // eslint-disable-line
242 ],
243 'Yesterday': [
244 moment().subtract(1, 'days'), // eslint-disable-line
245 moment().subtract(1, 'days') // eslint-disable-line
246 ],
247 'Last 7 Days': [
248 moment().subtract(6, 'days'), // eslint-disable-line
249 moment() // eslint-disable-line
250 ],
251 'Last 14 Days': [
252 moment().subtract(13, 'days'), // eslint-disable-line
253 moment() // eslint-disable-line
254 ],
255 'Last 30 Days': [
256 moment().subtract(29, 'days'), // eslint-disable-line
257 moment() // eslint-disable-line
258 ],
259 'This Month': [
260 moment().startOf('month'), moment().endOf('month') // eslint-disable-line
261 ],
262 'Last Month': [
263 moment().subtract(1, 'month').startOf('month'), // eslint-disable-line
264 moment().subtract(1, 'month').endOf('month') // eslint-disable-line
265 ]
266 };
267
268 s.ranges[ app.getAllTimeLabel() ] = app.getAllTimeRange();
269
270 }
271
272 }
273
274 },
275
276 /**
277 * Inits the date picker for most standard datepicker UIs.
278 *
279 * @since 1.8.1
280 *
281 */
282 initDatePicker: function ( datepickerClass = 'ranged', datepickerObject = false ) {
283
284 if ( datepickerObject ) {
285
286 var $the_datepicker = $reports.find( datepickerObject );
287
288 $the_datepicker.daterangepicker({
289 opens: 'left',
290 locale: {
291 format: 'Y/M/DD'
292 },
293 linkedCalendars: true,
294 showCustomRangeLabel: true,
295 alwaysShowCalendars: true,
296 ranges: s.ranges
297
298 }, function ( start, end, label ) { // eslint-disable-line
299
300 wpchar.debug('first');
301 wpchar.debug( $the_datepicker );
302
303 $the_datepicker.attr('data-start-date', start.format('Y/M/DD') );
304 $the_datepicker.attr('data-end-date', end.format('Y/M/DD') );
305
306 wpchar.debug( $the_datepicker );
307
308 s.datePickerStartDate = start.format('Y/M/DD');
309 s.datePickerEndDate = end.format('Y/M/DD');
310
311 wpchar.debug( app.getStartDateFromDatePicker() );
312 wpchar.debug( app.getEndDateFromDatePicker() );
313 wpchar.debug( elements.$topnav_datepicker );
314
315 wpchar.debug("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
316 });
317
318 } else if ( datepickerClass === 'ranged' ) {
319
320 $reports.find('input.charitable-datepicker-' + datepickerClass).daterangepicker({
321 opens: 'left',
322 locale: {
323 format: 'Y/M/DD'
324 },
325 linkedCalendars: true,
326 showCustomRangeLabel: true,
327 alwaysShowCalendars: true,
328 ranges: s.ranges
329
330 }, function ( start, end, label ) { // eslint-disable-line
331 elements.$topnav_datepicker.attr('data', 'start-date').val( start.format('Y/M/DD') );
332 elements.$topnav_datepicker.attr('data', 'end-date').val( end.format('Y/M/DD') );
333
334 s.datePickerStartDate = start.format('Y/M/DD');
335 s.datePickerEndDate = end.format('Y/M/DD');
336
337 wpchar.debug( app.getStartDateFromDatePicker() );
338 wpchar.debug( app.getEndDateFromDatePicker() );
339 wpchar.debug( elements.$topnav_datepicker );
340
341 wpchar.debug("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
342 });
343
344 $reports.find('input#charitable-reports-topnav-datepicker-comparefrom-lybunt.charitable-datepicker-' + datepickerClass).daterangepicker({
345 opens: 'left',
346 locale: {
347 format: 'Y/M/DD'
348 },
349 linkedCalendars: true,
350 showCustomRangeLabel: true,
351 alwaysShowCalendars: true,
352 ranges: s.ranges
353
354 }, function ( start, end, label ) { // eslint-disable-line
355 wpchar.debug('second');
356 wpchar.debug( start.format('Y/M/DD') );
357 wpchar.debug( end.format('Y/M/DD') );
358 elements.$topnav_datepicker_comparefrom.attr('data-start-date', start.format('Y/M/DD') );
359 elements.$topnav_datepicker_comparefrom.attr('data-end-date', end.format('Y/M/DD') );
360
361 s.datePickerCompareFromStartDate = start.format('Y/M/DD');
362 s.datePickerCompareFromEndDate = end.format('Y/M/DD');
363
364 wpchar.debug( app.getStartDateFromDatePicker() );
365 wpchar.debug( app.getEndDateFromDatePicker() );
366 wpchar.debug( elements.$topnav_datepicker_comparefrom );
367 wpchar.debug( this );
368 wpchar.debug( label );
369
370 wpchar.debug("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
371 });
372
373 $reports.find('input#charitable-reports-topnav-datepicker-compareto-lybunt.charitable-datepicker-' + datepickerClass).daterangepicker({
374 opens: 'left',
375 locale: {
376 format: 'Y/M/DD'
377 },
378 linkedCalendars: true,
379 showCustomRangeLabel: true,
380 alwaysShowCalendars: true,
381 ranges: s.ranges
382
383 }, function ( start, end, label ) { // eslint-disable-line
384 wpchar.debug('second');
385 wpchar.debug( start.format('Y/M/DD') );
386 wpchar.debug( end.format('Y/M/DD') );
387 elements.$topnav_datepicker_compareto.attr('data-start-date', start.format('Y/M/DD') );
388 elements.$topnav_datepicker_compareto.attr('data-end-date', end.format('Y/M/DD') );
389
390 s.datePickerCompareToStartDate = start.format('Y/M/DD');
391 s.datePickerCompareToEndDate = end.format('Y/M/DD');
392
393 wpchar.debug( app.getStartDateFromDatePicker() );
394 wpchar.debug( app.getEndDateFromDatePicker() );
395 wpchar.debug( elements.$topnav_datepicker_comparefrom );
396 wpchar.debug( this );
397 wpchar.debug( label );
398
399 wpchar.debug("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
400 });
401
402 } else {
403
404 wpchar.debug('using datepicker class: ' + $( 'input.' + datepickerClass ).val() );
405
406 var $the_datepicker = $reports.find( 'input.' + datepickerClass ); // eslint-disable-line
407
408 $the_datepicker.daterangepicker({
409 opens: 'left',
410 locale: {
411 format: 'Y/M/DD'
412 },
413 linkedCalendars: true,
414 showCustomRangeLabel: true,
415 alwaysShowCalendars: true,
416 ranges: s.ranges
417
418 }, function ( start, end, label ) { // eslint-disable-line
419
420 wpchar.debug('third');
421 wpchar.debug( $the_datepicker );
422
423 $the_datepicker.attr('data', 'start-date').val( start.format('Y/M/DD') );
424 $the_datepicker.attr('data', 'end-date').val( end.format('Y/M/DD') );
425
426 s.datePickerStartDate = start.format('Y/M/DD');
427 s.datePickerEndDate = end.format('Y/M/DD');
428
429 wpchar.debug( app.getStartDateFromDatePicker() );
430 wpchar.debug( app.getEndDateFromDatePicker() );
431 wpchar.debug( elements.$topnav_datepicker );
432
433 wpchar.debug("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
434 });
435
436 }
437
438 },
439
440 /**
441 * Element bindings.
442 *
443 * @since 1.8.1
444 */
445 bindUIActions: function () {
446
447 $reports.on('click', '.charitable-toggle', function (e) {
448 e.preventDefault();
449 // remove the focus from the button.
450 $(this).blur();
451 app.fieldSectionToggle($(this), 'click');
452 });
453
454 // Init all date pickers on the page.
455 $reports.find('.charitable-report-download-items').each(function () {
456 app.initDownloadButton($(this));
457 });
458
459 $reports.on('click', '#charitable-reports-filter-button[data-filter-type="overview"]', function (e) { // eslint-disable-line
460 e.preventDefault();
461
462 wpchar.debug('overview filter button clicked');
463
464 // Update the "print" button hidden field with the new value of the dropdown.
465 var campaign_id = parseInt( $('#report-campaign-filter').val() ) || -1;
466 $reports.find('form#charitable-overview-print input[name="campaign_id"]').val( campaign_id );
467 var category_id = parseInt( $('#report-category-filter').val() ) || 0;
468 $reports.find('form#charitable-overview-print input[name="category_id"]').val( category_id );
469
470 var start_date = app.getStartDateFromDatePicker();
471 var end_date = app.getEndDateFromDatePicker();
472
473 // if charitable-reports-topnav-datepicker exists, update the data attributes with the new dates.
474 if ( elements.$topnav_datepicker.length > 0 ) {
475 elements.$topnav_datepicker.attr('data-start-date', start_date );
476 elements.$topnav_datepicker.attr('data-end-date', end_date );
477 }
478 // Update the "print" button hidden field with the new value of the dropdown.
479 $reports.find('form#charitable-overview-print input[name="start_date"]').val( start_date );
480 $reports.find('form#charitable-overview-print input[name="end_date"]').val( end_date );
481
482 wpchar.debug( 'start_date: ' + start_date );
483 wpchar.debug( 'end_date: ' + end_date );
484
485 app.getOverviewReports();
486 });
487
488 /* advanced reports */
489
490 // selecting a value in the advanced report type dropdown.
491 $reports.on('change', '#report-advanced-type-filter', function (e) { // eslint-disable-line
492 e.preventDefault();
493 app.loadAdvancedReportUI( $(this).val() );
494 });
495
496 // clicking of the filter button.
497 $reports.on('click', '.charitable-advanced-filter-button a.button', function (e) { // eslint-disable-line
498 e.preventDefault();
499 app.getAdvancedReports( $( this ) );
500 });
501
502 $reports.on('click', '#charitable-reports-filter-button[data-filter-type="activity"]', function (e) { // eslint-disable-line
503 e.preventDefault();
504 app.getActivityReports();
505 });
506
507 $reports.on('click', '#charitable-reports-filter-button[data-filter-type="donor"]', function (e) { // eslint-disable-line
508 e.preventDefault();
509 app.getDonorReports();
510 });
511
512 $reports.on('change', '#report-campaign-filter', function (e) { // eslint-disable-line
513 // e.preventDefault();
514 app.checkCategoryDropdown();
515 });
516
517 /* dhashboard */
518
519 if ( app.isDashboardPage() ) {
520
521 $reports.on('change', '#report-date-range-filter', function (e) { // eslint-disable-line
522 e.preventDefault();
523
524 // Update the "print" button hidden field with the new value of the dropdown.
525 $reports.find('form#charitable-dashboard-print input[name="days"]').val( $(this).val() );
526
527 app.getDashboardReports();
528 });
529
530 }
531
532 },
533
534 /**
535 * Toggle field group visibility in the field sidebar.
536 *
537 * @since 1.8.1
538 *
539 * @param {mixed} el DOM element or jQuery object.
540 * @param {string} action Action.
541 */
542 fieldSectionToggle: function (el, action) {
543
544 var $this = $(el),
545 $nearestContainer = $this.closest('.charitable-container'),
546 $toggleGroup = $nearestContainer.find('.charitable-toggle-container'),
547 sectionName = $nearestContainer.data('section-name'),
548 $icon = $this.find('i'),
549 cookieName = 'charitable_reports_section_' + sectionName;
550
551 if (action === 'click') {
552
553 $icon.toggleClass('charitable-angle-right');
554
555 $toggleGroup.stop().slideToggle('', function () {
556 $nearestContainer.toggleClass('charitable-closed');
557 if ($nearestContainer.hasClass('charitable-closed')) {
558 $nearestContainer.removeClass('charitable-open');
559 wpCookies.remove(cookieName);
560 } else {
561 wpCookies.set(cookieName, 'true', 2592000); // 1 month
562 }
563 });
564
565 return;
566 }
567
568 },
569
570 /* dynamic UI updates */
571
572 /**
573 * This loads the UI/data for advanced reports, usually when a filter has been applied on the page.
574 *
575 * @since 1.8.1
576 *
577 * @param {string} slug The slug of the advanced report.
578 *
579 */
580 loadAdvancedReportUI: function ( slug = '' ) {
581
582 wpchar.debug( charitable_reporting );
583 wpchar.debug( charitable_reporting.advanced_reports[slug].title );
584 wpchar.debug( slug );
585
586 // show the appropriate advanced report UI (date pickers and filter buttons).
587 $reports.find('.charitable-advanced-date-picker, .charitable-advanced-filter-button').addClass('charitable-hidden');
588 $reports.find('.charitable-advanced-date-picker[data-report-type="' + slug + '"]').removeClass('charitable-hidden');
589 $reports.find('.charitable-advanced-filter-button[data-report-type="' + slug + '"]').removeClass('charitable-hidden');
590
591 app.disableUI();
592 app.reportUILoadingOn();
593
594 var data = {
595 action: 'charitable_report_advanced_ui',
596 nonce: charitable_reporting.nonce,
597 report_type: slug,
598 report_title: charitable_reporting.advanced_reports[slug].title,
599 report_description: charitable_reporting.advanced_reports[slug].description,
600 start_date: s.datePickerStartDate, // this would be a default date?
601 end_date: s.datePickerEndDate // this would be a default date?
602 };
603
604 $.post( charitable_reporting.ajax_url, data, function (response) {
605
606 if (response.success) { // check and see if HTML donation_breakdown is returned and if so, update the breakdown table.
607
608 wpchar.debug('response success');
609
610 // use slug as a dynamic variable to access the charitable_reporting object.
611 if ( typeof charitable_reporting.advanced_reports[slug] !== 'undefined' ) {
612 // update the title and description of the title cards, if the content exists in the localized strings JS in the footer.
613 $reports.find('.charitable-title-card-content h2').html( charitable_reporting.advanced_reports[slug].title );
614 $reports.find('.charitable-title-card-content div.charitable-report-description').html( charitable_reporting.advanced_reports[slug].description );
615 }
616
617 if ( response.data.html ) {
618 $reports.find('#charitable-report-advanced-container').html( response.data.html );
619 } else {
620 $reports.find('#charitable-report-advanced-container').html( '' );
621 }
622
623 app.enableUI();
624 app.reportUILoadingOff();
625
626 }
627
628 });
629
630 // init the date pickers for the advanced report UI.
631 app.initDatePicker( false, '.charitable-advanced-date-picker[data-report-type="' + slug + '"] input.charitable-reports-datepicker' );
632
633 // populate the start/end date vars.
634 if ( slug !== 'lybunt' ) {
635 s.datePickerStartDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + slug + '"]').find('input.charitable-reports-datepicker').data('start-date');
636 s.datePickerEndDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + slug + '"]').find('input.charitable-reports-datepicker').data('end-date');
637 } else {
638 s.datePickerCompareFromStartDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + slug + '"]').find('input#charitable-reports-topnav-datepicker-comparefrom-lybunt').data('start-date');
639 s.datePickerCompareFromEndDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + slug + '"]').find('input#charitable-reports-topnav-datepicker-comparefrom-lybunt').data('end-date');
640 s.datePickerCompareToStartDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + slug + '"]').find('input#charitable-reports-topnav-datepicker-compareto-lybunt').data('start-date');
641 s.datePickerCompareToEndDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + slug + '"]').find('input#charitable-reports-topnav-datepicker-compareto-lybunt').data('end-date');
642 }
643
644 },
645
646 /**
647 * This laods the UI/data for dashboard reports, usually when a filter has been applied on the page.
648 *
649 * @since 1.8.1
650 *
651 */
652 getDashboardReports: function () {
653
654 var data = {
655 action: 'charitable_report_dashboard_data',
656 nonce: charitable_reporting.nonce,
657 start_date: app.getDashboardStartDate(),
658 end_date: app.getDashboardEndDate(),
659 days: app.getDashboardDays()
660 };
661
662 app.disableUI();
663 app.reportUILoadingOn();
664
665 $.post( charitable_reporting.ajax_url, data, function (response) {
666
667 if (response.success) { // check and see if HTML donation_breakdown is returned and if so, update the breakdown table.
668
669 wpchar.debug('dashboard response success');
670
671 showGrowthToolsNotice = false;
672
673 if ( response.data.html.charitable_cards ) {
674 wpchar.debug( 'updating dashboard with: ' + response.data.html.charitable_cards );
675 $reports.find('#charitable-dashboard-report-cards').html( response.data.html.charitable_cards );
676 }
677
678 if ( response.data.html.charitable_reports ) {
679 wpchar.debug( 'updating dashboard with: ' + response.data.html.charitable_reports );
680 $reports.find('#charitable-dashboard-report-sections').html( response.data.html.charitable_reports );
681 }
682
683 // if form#charitable-dashboard-print exists, update the hidden fields with the new dates.
684 if ( $reports.find('form#charitable-dashboard-print').length > 0 ) {
685 $reports.find('form#charitable-dashboard-print input[name="start_date"]').val( response.data.start_date );
686 $reports.find('form#charitable-dashboard-print input[name="end_date"]').val( response.data.end_date );
687 $reports.find('form#charitable-dashboard-print input[name="days"]').val( response.data.days );
688 }
689
690 wpchar.debug( charts.headlineChart );
691 wpchar.debug( response.data.headline_chart_options.donation_axis );
692 wpchar.debug( response.data.headline_chart_options.date_axis );
693 // check and see if axis data is returned and if so, update the headline chart.
694 if ( charts.headlineChart && response.data.headline_chart_options.donation_axis && response.data.headline_chart_options.date_axis ) {
695 charts.headlineChart.updateOptions({
696 series: [
697 {
698 name: 'Donations',
699 data: response.data.headline_chart_options.donation_axis
700 }
701 ],
702 xaxis: {
703 categories: response.data.headline_chart_options.date_axis
704 }
705 });
706
707 // determine if the donation axis is all zeros.
708 var isAllZero = response.data.headline_chart_options.donation_axis.every( function ( val ) {
709 return val === 0;
710 });
711
712 // check to see if the donation axis is empty or has all zeros.
713 if ( response.data.headline_chart_options.donation_axis.length === 0 || isAllZero === false ) {
714 showGrowthToolsNotice = false;
715 } else{
716 showGrowthToolsNotice = true;
717 }
718 if ( showGrowthToolsNotice && $( '.charitable-growth-tools-notice' ).length ) {
719 $( '.charitable-growth-tools-notice' ).removeClass( 'charitable-hidden' );
720 } else {
721 $( '.charitable-growth-tools-notice' ).addClass( 'charitable-hidden' );
722 }
723 }
724
725 app.enableUI();
726 app.reportUILoadingOff();
727
728 }
729
730 });
731
732 },
733
734 /**
735 * This laods the UI/data for report:overview reports, usually when a filter has been applied on the page.
736 *
737 * @since 1.8.1
738 *
739 */
740 getOverviewReports: function () {
741
742 var data = {
743 action: 'charitable_report_overview_data',
744 nonce: charitable_reporting.nonce,
745 start_date: app.getStartDateFromDatePicker(),
746 end_date: app.getEndDateFromDatePicker(),
747 campaign_id: parseInt( $('#report-campaign-filter').val() ) || 0,
748 campaign_category_id: parseInt( $('#report-category-filter').val() ) || 0,
749 filter: $('#report-date-range-filter').val()
750 };
751
752 app.disableUI();
753 app.reportUILoadingOn();
754 app.clearReportError();
755
756 $.post(charitable_reporting.ajax_url, data, function (response) {
757
758 // A request that came back but reported failure must not leave the
759 // previous range's figures on screen under the new date range.
760 if ( ! response || ! response.success ) {
761
762 wpchar.debug('response failure');
763
764 app.showReportError( app.getString( 'i18n_report_error', 'This report could not be loaded. Please try again.' ) );
765
766 return;
767 }
768
769 if (response.success) { // check and see if HTML donation_breakdown is returned and if so, update the breakdown table.
770
771 wpchar.debug('response success');
772
773 if ( response.data.html.donation_breakdown ) {
774 elements.$donations_breakdown_list.html(response.data.html.donation_breakdown);
775 }
776
777 // check and see if axis data is returned and if so, update the headline chart.
778 if ( charts.headlineChart && response.data.headline_chart_options.donation_axis && response.data.headline_chart_options.date_axis ) {
779 charts.headlineChart.updateOptions({
780 series: [
781 {
782 name: 'Donations',
783 data: response.data.headline_chart_options.donation_axis
784 }
785 ],
786 xaxis: {
787 categories: response.data.headline_chart_options.date_axis
788 }
789 });
790 }
791
792 if (response.data.donation_amount) {
793 elements.$top_donation_amount.html(response.data.donation_amount);
794 }
795
796 // if donation_count was returned, update the donation count.
797 if ( response.data.donation_count ) {
798 elements.$top_donation_count.html(response.data.donation_count);
799 } else {
800 elements.$top_donation_count.html('0');
801 }
802
803 if ( response.data.donation_average ) {
804 elements.$top_donation_average.html(response.data.donation_average);
805 } else {
806 elements.$top_donation_average.html('0');
807 }
808
809 // if donor_count was returned, update the donor count.
810 if ( response.data.donors_count ) {
811 elements.$top_donation_donors_count.html( response.data.donors_count );
812 } else {
813 elements.$top_donation_donors_count.html('0');
814 }
815
816 if ( response.data.refund_total_amount ) {
817 elements.$top_charitable_refund_total_amount.html(response.data.refund_total_amount);
818 }
819
820 if ( response.data.refund_count ) {
821 elements.$top_charitable_refund_count.html(response.data.refund_count);
822 } else {
823 elements.$top_charitable_refund_count.html('0');
824 }
825
826 if ( response.data.html.payment_methods_list ) {
827 elements.$payment_methods_list.html( response.data.html.payment_methods_list );
828 }
829
830 if ( charts.paymentMethodsChart && response.data.payment_methods_chart.payment_percentages && response.data.payment_methods_chart.payment_labels ) {
831 charts.paymentMethodsChart.updateOptions( { series: response.data.payment_methods_chart.payment_percentages, labels: response.data.payment_methods_chart.payment_labels } );
832 }
833
834 if ( response.data.html.activities ) {
835 wpchar.debug( 'updating activities with: ' + response.data.html.activities );
836 $reports.find('.charitable-activity-report .charitable-report-ui .the-list').html(response.data.html.activities);
837 }
838
839 if ( response.data.html.top_donors_list ) {
840 wpchar.debug( 'updating top donors with: ' + response.data.html.top_donors_list );
841 $reports.find('.charitable-top-donors-report .charitable-report-ui').html(response.data.html.top_donors_list);
842 }
843
844 if ( response.data.html.top_campaigns_list ) {
845 elements.$top_campaigns_list.html(response.data.html.top_campaigns_list);
846 }
847
848 // update the "download csv" form.
849 $reports.find('#charitable-donations-breakdown-download-form input[name="start_date"]').val( app.getStartDateFromDatePicker() );
850 $reports.find('#charitable-donations-breakdown-download-form input[name="end_date"]').val( app.getEndDateFromDatePicker() );
851 $reports.find('#charitable-donations-breakdown-download-form input[name="campaign_id"]').val( parseInt( $('#report-campaign-filter').val() ) );
852 $reports.find('#charitable-donations-breakdown-download-form input[name="category_id"]').val( parseInt( $('#report-category-filter').val() ) );
853
854 app.enableUI();
855 app.reportUILoadingOff();
856 app.checkCategoryDropdown();
857
858 }
859
860 }).fail(function (jqXHR, textStatus) {
861
862 /**
863 * Without this, a request killed by the server (a 504 at
864 * max_execution_time on a long date range) never ran a callback at
865 * all: the UI stayed disabled and dimmed forever, and the cards kept
866 * showing the PREVIOUS range's figures while the date field showed
867 * the new one. An all-time total could be read as last week's.
868 */
869 wpchar.debug('overview request failed: ' + textStatus);
870
871 app.showReportError( app.getRequestFailureMessage( jqXHR, textStatus ) );
872
873 });
874
875
876 },
877
878 /**
879 * This laods the UI/data for advanced reports, usually when a filter has been applied on the page.
880 *
881 * @since 1.8.1
882 *
883 * @param {string} button Button element clicked, which we use to get the report type.
884 *
885 */
886 getAdvancedReports: function ( $button = false ) {
887
888 var report_type = $button.parent().data('report-type'),
889 data = {};
890
891 if ( 'lybunt' === report_type ) {
892
893 var datePickerCompareFromStartDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + report_type + '"]').find('input#charitable-reports-topnav-datepicker-comparefrom-lybunt').attr('data-start-date'),
894 datePickerCompareFromEndDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + report_type + '"]').find('input#charitable-reports-topnav-datepicker-comparefrom-lybunt').attr('data-end-date'),
895 datePickerCompareToStartDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + report_type + '"]').find('input#charitable-reports-topnav-datepicker-compareto-lybunt').attr('data-start-date'),
896 datePickerCompareToEndDate = $reports.find('.charitable-advanced-date-picker[data-report-type="' + report_type + '"]').find('input#charitable-reports-topnav-datepicker-compareto-lybunt').attr('data-end-date');
897
898 data = {
899 action: 'charitable_report_advanced_data',
900 nonce: charitable_reporting.nonce,
901 report_type: report_type,
902 compare_from_start_date: datePickerCompareFromStartDate,
903 compare_from_end_date: datePickerCompareFromEndDate,
904 compare_to_start_date: datePickerCompareToStartDate,
905 compare_to_end_date: datePickerCompareToEndDate
906 };
907
908 } else {
909
910 var start_date = s.datePickerStartDate, // $reports.find('.charitable-advanced-date-picker[data-report-type="' + report_type + '"]').find('input.charitable-reports-datepicker').data('start-date'),
911 end_date = s.datePickerEndDate; // $reports.find('.charitable-advanced-date-picker[data-report-type="' + report_type + '"]').find('input.charitable-reports-datepicker').data('end-date');
912
913 data = {
914 action: 'charitable_report_advanced_data',
915 nonce: charitable_reporting.nonce,
916 report_type: report_type,
917 start_date: start_date,
918 end_date: end_date
919 };
920
921 }
922
923 app.disableUI();
924 app.reportUILoadingOn();
925
926 $.post( charitable_reporting.ajax_url, data, function (response) {
927
928 if (response.success) { // check and see if HTML donation_breakdown is returned and if so, update the breakdown table.
929
930 wpchar.debug('response success');
931
932 // use slug as a dynamic variable to access the charitable_reporting object.
933 if ( typeof charitable_reporting.advanced_reports[report_type] !== 'undefined' ) {
934 // update the title and description of the title cards, if the content exists in the localized strings JS in the footer.
935 $reports.find('.charitable-title-card-content h2').html( charitable_reporting.advanced_reports[report_type].title );
936 $reports.find('.charitable-title-card-content div.charitable-report-description').html( charitable_reporting.advanced_reports[report_type].description );
937 }
938
939 if ( response.data.html ) {
940 $reports.find('#charitable-report-advanced-container').html( response.data.html );
941 }
942
943 app.enableUI();
944 app.reportUILoadingOff();
945
946 }
947
948 });
949
950 },
951
952 /**
953 * This laods the UI/data for activity reports.
954 *
955 * @since 1.8.1
956 *
957 */
958 getActivityReports: function () {
959
960 var data = {
961 action: 'charitable_report_activity_data',
962 nonce: charitable_reporting.nonce,
963 start_date: app.getStartDateFromDatePicker(),
964 end_date: app.getEndDateFromDatePicker(),
965 campaign_id: parseInt( $('#report-campaign-filter').val() ),
966 activity_type: $('#report-activity-type-filter').val()
967 };
968
969 app.disableUI();
970 app.reportUILoadingOn();
971
972 $.post(charitable_reporting.ajax_url, data, function (response) {
973
974 if (response.success) { // check and see if HTML donation_breakdown is returned and if so, update the breakdown table.
975
976 wpchar.debug('response success');
977
978 if (response.data.html.activities) {
979 wpchar.debug( 'updating activities with: ' + response.data.html.activities );
980 $reports.find('.charitable-the-list-container').html(response.data.html.activities);
981 }
982
983 // update the "download csv" form.
984 $reports.find('#charitable-activity-download-form input[name="start_date"]').val( app.getStartDateFromDatePicker() );
985 $reports.find('#charitable-activity-download-form input[name="end_date"]').val( app.getEndDateFromDatePicker() );
986 $reports.find('#charitable-activity-download-form input[name="campaign_id"]').val( parseInt( $('#report-campaign-filter').val() ) );
987 $reports.find('#charitable-activity-download-form input[name="activity_type"]').val( $('#report-activity-type-filter').val() );
988
989 app.enableUI();
990 app.reportUILoadingOff();
991
992 }
993
994 });
995
996 },
997
998 /**
999 * This laods the UI/data for donor reports.
1000 *
1001 * @since 1.8.1
1002 *
1003 */
1004 getDonorReports: function () {
1005
1006 var data = {
1007 action: 'charitable_report_donor_data',
1008 nonce: charitable_reporting.nonce,
1009 report_type: $('#report-donor-type-filter').val(),
1010 limit: $('#report-donor-limit').val(),
1011 offset: $('#report-donor-offset').val(),
1012 ppage: $('#report-donor-ppage').val()
1013 };
1014
1015 app.disableUI();
1016 app.reportUILoadingOn();
1017
1018 $.post(charitable_reporting.ajax_url, data, function (response) {
1019
1020 if (response.success) { // check and see if HTML donation_breakdown is returned and if so, update the breakdown table.
1021
1022 if ( response.data.html.donors ) {
1023 wpchar.debug( 'updating donors with: ' + response.data.html.donors );
1024 $reports.find('#charitable-report-donor-container').html( response.data.html.donors );
1025 }
1026
1027 if ( response.data.html.title_card ) {
1028 $reports.find('#charitable-report-donor-title-card').html( response.data.html.title_card );
1029 }
1030
1031 app.enableUI();
1032 app.reportUILoadingOff();
1033
1034 }
1035
1036 });
1037
1038 },
1039
1040 initPagination: function () {
1041
1042 if ( $reports.find('.charitable-reports-pagination-nav').length === 0 ) {
1043 return;
1044 }
1045
1046 $reports.on('click', '.charitable-reports-pagination-nav .page-item a.page-link', function (e) { // eslint-disable-line
1047 e.preventDefault();
1048
1049 var $this = $(this),
1050 limit = $this.data('limit'),
1051 offset = $this.data('offset'),
1052 ppage = $this.data('ppage'),
1053 total_pages = $this.data('total-pages'),
1054 current_page = $this.data('current-page'),
1055 report_type = $this.data('report-type'),
1056 page = $this.data('page'),
1057 tab = $this.data('tab');
1058
1059 var data = {
1060 action: 'charitable_report_donor_data_pagination',
1061 nonce: charitable_reporting.nonce,
1062 limit: limit,
1063 offset: offset,
1064 ppage: ppage,
1065 total_pages: total_pages,
1066 current_page: current_page,
1067 report_type: report_type,
1068 page: page,
1069 tab: tab
1070 };
1071
1072 $.post(charitable_reporting.ajax_url, data, function (response) {
1073
1074 if (response.success) { // check and see if HTML donation_breakdown is returned and if so, update the breakdown table.
1075
1076 if ( response.data.html.donors ) {
1077 $reports.find('#charitable-report-donor-container').html( response.data.html.donors );
1078 }
1079
1080 }
1081
1082 });
1083
1084 });
1085
1086 },
1087
1088 /**
1089 * This inits the "headline" chart for a page.
1090 *
1091 * @since 1.8.1
1092 * @since 1.8.1.6 - Added check for empty donation axis and animationEnd event.
1093 *
1094 */
1095 initHeadlineChart: function () {
1096
1097 if ( $('#charitable-headline-graph').length === 0 ) {
1098 this.return;
1099 }
1100
1101 var enableToolTips = false;
1102
1103 if ( typeof charitable_reporting.headline_chart_options.enable_tooltips !== 'undefined' && false !== charitable_reporting.headline_chart_options.enable_tooltips ) {
1104 enableToolTips = true;
1105 }
1106
1107 var headlineChartOptions = {
1108 chart: {
1109 type: 'area',
1110 width: '100%',
1111 height: 400,
1112 foreColor: "#757781",
1113 toolbar: {
1114 autoSelected: "pan",
1115 show: false
1116 },
1117 events: {
1118 animationEnd: function ( chartContext, options ) {
1119 // if the goal tools notice is not visible, show it.
1120 if ( showGrowthToolsNotice && $( '.charitable-growth-tools-notice' ).length ) {
1121 $( '.charitable-growth-tools-notice' ).removeClass( 'charitable-hidden' );
1122 }
1123 }
1124 }
1125 },
1126 series: [
1127 {
1128 name: 'Donations',
1129 data: charitable_reporting.headline_chart_options.donation_axis
1130 }
1131 ],
1132 colors: ["#5AA15226"],
1133 grid: {
1134 borderColor: "#C9D4CA",
1135 clipMarkers: false,
1136 yaxis: {
1137 lines: {
1138 show: true
1139 }
1140 }
1141 },
1142 stroke: {
1143 width: 3,
1144 colors: ["#5AA152"]
1145 },
1146 fill: {
1147 type: "solid"
1148 },
1149 tooltip: {
1150 enabled: enableToolTips,
1151 theme: "dark"
1152 },
1153 dataLabels: {
1154 enabled: false
1155 },
1156 markers: {
1157 size: 5,
1158 colors: ["#FFFFFF"],
1159 strokeColor: "#5AA152",
1160 strokeWidth: 4
1161 },
1162 xaxis: {
1163 categories: charitable_reporting.headline_chart_options.date_axis
1164 },
1165 yaxis: {
1166 labels: {
1167 formatter: (value) => {
1168 // Format currency values using the configured decimal count.
1169 // Currency symbol is already decoded in PHP, so use it directly.
1170 var decimalCount = typeof charitable_reporting.decimal_count !== 'undefined'
1171 ? charitable_reporting.decimal_count
1172 : 2;
1173 return charitable_reporting.currency_symbol + value.toFixed(decimalCount);
1174 }
1175 },
1176 min: 0
1177 }
1178 }
1179
1180 charts.headlineChart = new ApexCharts(document.querySelector("#charitable-headline-graph"), headlineChartOptions); // eslint-disable-line
1181
1182 // check if array/value exists.
1183 if ( typeof charitable_reporting.headline_chart_options.donation_axis !== 'undefined' ) {
1184 // check if array has all zeros.
1185 if ( charitable_reporting.headline_chart_options.donation_axis.every( function (i) { return i === 0; } ) ) {
1186 showGrowthToolsNotice = true;
1187 } else {
1188 showGrowthToolsNotice = false;
1189 }
1190 }
1191
1192 },
1193
1194 /**
1195 * This inits the "payment" chart for a page, usually located on the dashboard.
1196 *
1197 * @since 1.8.1
1198 *
1199 */
1200 initPaymentChart: function () {
1201
1202 if ( ! app.isDashboardPage() ) {
1203 this.return;
1204 }
1205
1206 if ( $('#charitable-payment-methods-graph').length === 0 ) {
1207 this.return;
1208 }
1209
1210 if ( typeof charitable_reporting.payment_methods_chart_options === 'undefined' ) {
1211 this.return;
1212 }
1213
1214 // Color mapping for payment methods by gateway key.
1215 // This ensures consistent colors regardless of order.
1216 // Colors match the CSS legend dot colors in charitable-admin.scss.
1217 var paymentMethodColors = {
1218 'stripe': '#5AA152', // Green (matches CSS)
1219 'offline': '#9e36f9', // Purple (matches CSS)
1220 'manual': '#F99E36', // Orange (matches CSS)
1221 'paypal': '#2B66D1', // Blue (matches CSS)
1222 'square_core': '#d21561', // Pink/Red (unique color for Square)
1223 'square': '#d21561' // Legacy Square support
1224 };
1225
1226 // Default color palette for unknown payment methods.
1227 var defaultColors = [
1228 '#d21561', '#9e36f9', '#F99E36', '#2B66D1', '#5AA152'
1229 ];
1230
1231 // Build colors array based on payment method keys.
1232 var paymentKeys = typeof charitable_reporting.payment_methods_chart_options.payment_keys !== 'undefined'
1233 ? charitable_reporting.payment_methods_chart_options.payment_keys
1234 : [];
1235 var colors = [];
1236 var colorIndex = 0;
1237
1238 for ( var i = 0; i < paymentKeys.length; i++ ) {
1239 var key = paymentKeys[i];
1240 if ( key && paymentMethodColors[key] ) {
1241 colors.push( paymentMethodColors[key] );
1242 } else if ( key ) {
1243 // For unknown payment methods, use a color from the default palette.
1244 colors.push( defaultColors[colorIndex % defaultColors.length] );
1245 colorIndex++;
1246 } else {
1247 // Empty key (usually the trailing empty entry), use first default color.
1248 colors.push( defaultColors[0] );
1249 }
1250 }
1251
1252 // If no colors were generated (fallback), use the default array.
1253 if ( colors.length === 0 ) {
1254 colors = defaultColors;
1255 }
1256
1257 var paymentMethodsChartOptions = {
1258 series: charitable_reporting.payment_methods_chart_options.payment_percentages,
1259 labels: charitable_reporting.payment_methods_chart_options.payment_labels,
1260 colors: colors,
1261 chart: {
1262 type: 'donut',
1263 width: '75%',
1264 toolbar: {
1265 autoSelected: "pan",
1266 show: false
1267 }
1268 },
1269 dataLabels: {
1270 enabled: false
1271 },
1272 yaxis: {
1273 labels: {
1274 formatter: (value) => {
1275 return value + '%'
1276 }
1277 }
1278 },
1279 plotOptions: {
1280 pie: {
1281 donut: {
1282 size: '80%'
1283 }
1284 }
1285 },
1286 total: {
1287 show: true,
1288 showAlways: true,
1289 label: 'Total',
1290 fontSize: '22px',
1291 fontFamily: 'Helvetica, Arial, sans-serif',
1292 fontWeight: 600,
1293 color: '#373d3f',
1294 formatter: function (w) {
1295 return w.globals.seriesTotals.reduce((a, b) => {
1296 return a + b
1297 }, 0)
1298 }
1299 },
1300 legend: {
1301 show: false,
1302 showForSingleSeries: true,
1303 showForNullSeries: true,
1304 showForZeroSeries: true,
1305 position: 'bottom',
1306 horizontalAlign: 'center',
1307 floating: false,
1308 fontSize: '14px',
1309 fontFamily: 'Helvetica, Arial',
1310 fontWeight: 400,
1311 inverseOrder: true,
1312 customLegendItems: [],
1313 offsetX: 0,
1314 offsetY: 0,
1315 labels: {
1316 useSeriesColors: false
1317 },
1318 markers: {
1319 width: 12,
1320 height: 12,
1321 strokeWidth: 0,
1322 strokeColor: '#fff',
1323 radius: 12,
1324 offsetX: 0,
1325 offsetY: 0,
1326 customHTML: function () {
1327 return '<span class="custom-marker">test</span>'
1328 }
1329 },
1330 itemMargin: {
1331 horizontal: 5,
1332 vertical: 0
1333 }
1334 }
1335 }
1336
1337 charts.paymentMethodsChart = new ApexCharts(document.querySelector("#charitable-payment-methods-graph"), paymentMethodsChartOptions); // eslint-disable-line
1338
1339 },
1340
1341 /**
1342 * Init the charts on the page.
1343 *
1344 * @since 1.8.1
1345 *
1346 */
1347 initCharts: function () {
1348
1349 app.initHeadlineChart();
1350 app.initPaymentChart();
1351
1352 },
1353
1354 /**
1355 * Render the headline chart on the page.
1356 *
1357 * @since 1.8.1
1358 *
1359 */
1360 renderHeadlineChart: function () {
1361
1362 if ($('#charitable-headline-graph').length === 0) {
1363 return;
1364 }
1365
1366 charts.headlineChart.render();
1367
1368 },
1369
1370 /**
1371 * Render the payment chart on the page.
1372 *
1373 * @since 1.8.1
1374 *
1375 */
1376 renderPaymentChart: function () {
1377
1378 if ($('#charitable-payment-methods-graph').length === 0) {
1379 return;
1380 }
1381
1382 charts.paymentMethodsChart.render();
1383
1384 },
1385
1386 /**
1387 * Render the charts on the page.
1388 *
1389 * @since 1.8.1
1390 *
1391 */
1392 renderCharts: function () {
1393
1394 app.renderHeadlineChart();
1395 app.renderPaymentChart();
1396
1397 },
1398
1399 /* UI and Misc */
1400
1401 /**
1402 * Disables the UI on the page.
1403 *
1404 * @since 1.8.1
1405 *
1406 */
1407 disableUI: function () {
1408
1409 $reports.find('a, button, input, select').attr('disabled', 'disabled').addClass('charitable-disabled');
1410
1411 },
1412
1413 /**
1414 * Enables the UI on the page.
1415 *
1416 * @since 1.8.1
1417 *
1418 */
1419 enableUI: function () {
1420
1421 $reports.find('a, button, input, select').removeAttr('disabled').removeClass('charitable-disabled');
1422
1423 },
1424
1425 /**
1426 * Shows the loading indicator on the page.
1427 *
1428 * @since 1.8.1
1429 *
1430 */
1431 reportUILoadingOn: function () {
1432
1433 $reports.find('.charitable-report-ui, .charitable-activity-list-container').addClass('charitable-section-loading');
1434
1435 },
1436
1437 /**
1438 * Hides the loading indicator on the page.
1439 *
1440 * @since 1.8.1
1441 *
1442 */
1443 reportUILoadingOff: function () {
1444
1445 $reports.find('.charitable-report-ui, .charitable-activity-list-container').removeClass('charitable-section-loading');
1446
1447 },
1448
1449 /**
1450 * Reads a localized string, falling back to English if the string was not
1451 * localized (older report screens localize their own subset).
1452 *
1453 * @since 1.8.12.2
1454 *
1455 * @param {string} key The key in charitable_reporting.
1456 * @param {string} fallback Text to use when the key is absent.
1457 *
1458 * @return {string} The string to display.
1459 */
1460 getString: function ( key, fallback ) {
1461
1462 if ( typeof charitable_reporting !== 'undefined' && charitable_reporting[ key ] ) {
1463 return charitable_reporting[ key ];
1464 }
1465
1466 return fallback;
1467
1468 },
1469
1470 /**
1471 * Builds the message for a request that failed at the HTTP level.
1472 *
1473 * @since 1.8.12.2
1474 *
1475 * @param {Object} jqXHR The jQuery XHR object.
1476 * @param {string} textStatus The jQuery status string.
1477 *
1478 * @return {string} The message to display.
1479 */
1480 getRequestFailureMessage: function ( jqXHR, textStatus ) {
1481
1482 var status = ( jqXHR && jqXHR.status ) ? jqXHR.status : 0;
1483
1484 // 504/502 is a gateway timeout; status 0 is a connection dropped or a
1485 // request aborted, which is what a request killed at max_execution_time
1486 // looks like from the browser. All three mean "took too long".
1487 if ( 'timeout' === textStatus || 504 === status || 502 === status || 0 === status ) {
1488 return app.getString( 'i18n_report_timeout', 'This report took too long to load and was stopped by the server. Try a shorter date range.' );
1489 }
1490
1491 return app.getString( 'i18n_report_error', 'This report could not be loaded. Please try again.' );
1492
1493 },
1494
1495 /**
1496 * Returns the container used for report-level errors, creating it if the
1497 * current report template does not already provide one.
1498 *
1499 * @since 1.8.12.2
1500 *
1501 * @return {Object} The jQuery-wrapped error container.
1502 */
1503 getReportErrorContainer: function () {
1504
1505 var $error = $reports.find('#charitable-report-error');
1506
1507 if ( $error.length === 0 ) {
1508 $error = $('<div id="charitable-report-error" class="notice notice-error charitable-report-error"><p></p></div>');
1509 $reports.prepend( $error );
1510 }
1511
1512 return $error;
1513
1514 },
1515
1516 /**
1517 * Hides any error left over from a previous request.
1518 *
1519 * @since 1.8.12.2
1520 *
1521 */
1522 clearReportError: function () {
1523
1524 $reports.find('#charitable-report-error').hide();
1525
1526 app.unflagStaleData();
1527
1528 },
1529
1530 /**
1531 * Marks every data panel as not reflecting the requested date range.
1532 *
1533 * Blanking the headline cards is not enough on its own. The chart, the
1534 * breakdown table, the payment methods, the activity list and the top
1535 * donors/campaigns panels all still hold the PREVIOUS range's data, while
1536 * the date field shows the range the user actually asked for - which is the
1537 * misreading the original report described.
1538 *
1539 * These are flagged rather than emptied so the earlier figures stay
1540 * available for reference; the error notice above says why they are dimmed.
1541 * The opacity is applied inline so this needs no stylesheet change.
1542 *
1543 * @since 1.8.12.2
1544 *
1545 */
1546 flagStaleData: function () {
1547
1548 $reports.find('.charitable-report-ui')
1549 .addClass('charitable-section-stale')
1550 .css('opacity', '0.4');
1551
1552 },
1553
1554 /**
1555 * Clears the stale flag, ahead of a fresh request.
1556 *
1557 * @since 1.8.12.2
1558 *
1559 */
1560 unflagStaleData: function () {
1561
1562 $reports.find('.charitable-section-stale')
1563 .removeClass('charitable-section-stale')
1564 .css('opacity', '');
1565
1566 },
1567
1568 /**
1569 * Shows a report-level error and clears the headline figures.
1570 *
1571 * @since 1.8.12.2
1572 *
1573 * @param {string} message The message to display.
1574 *
1575 */
1576 showReportError: function ( message ) {
1577
1578 app.getReportErrorContainer().find('p').text( message ).end().show();
1579
1580 app.blankHeadlineFigures();
1581 app.flagStaleData();
1582
1583 // Always hand the UI back, or the screen stays dimmed and disabled with
1584 // no way to try a different range.
1585 app.enableUI();
1586 app.reportUILoadingOff();
1587
1588 // enableUI() clears charitable-disabled from everything, including the
1589 // category dropdown that checkCategoryDropdown() owns. The success path
1590 // re-asserts it for the same reason.
1591 app.checkCategoryDropdown();
1592
1593 },
1594
1595 /**
1596 * Replaces the headline figures with a placeholder.
1597 *
1598 * Deliberate: a failed request used to leave the previous range's numbers in
1599 * the cards while the date field showed the range the user actually asked
1600 * for. A dash is unhelpful, but unlike a stale number it cannot be misread.
1601 *
1602 * @since 1.8.12.2
1603 *
1604 */
1605 blankHeadlineFigures: function () {
1606
1607 var placeholder = '&mdash;';
1608
1609 $.each( [
1610 elements.$top_donation_amount,
1611 elements.$top_donation_count,
1612 elements.$top_donation_average,
1613 elements.$top_donation_donors_count,
1614 elements.$top_charitable_refund_total_amount,
1615 elements.$top_charitable_refund_count
1616 ], function ( index, $el ) {
1617
1618 if ( $el && $el.length ) {
1619 $el.html( placeholder );
1620 }
1621
1622 } );
1623
1624 },
1625
1626 /**
1627 * Util function that gets the start dates from the date picker.
1628 *
1629 * @since 1.8.1
1630 *
1631 */
1632 getStartDateFromDatePicker: function () {
1633
1634 return s.datePickerStartDate; // elements.$topnav_datepicker.data('start-date');
1635
1636 },
1637
1638 /**
1639 * Util function that gets the end dates from the date picker.
1640 *
1641 * @since 1.8.1
1642 *
1643 */
1644 getEndDateFromDatePicker: function () {
1645
1646 return s.datePickerEndDate; // elements.$topnav_datepicker.data('end-date');
1647
1648 },
1649
1650 /**
1651 * Util function that gets the start date "compare" from the date picker.
1652 *
1653 * @since 1.8.1
1654 *
1655 */
1656 getStartDateFromCompareFromDatePicker: function () {
1657
1658 return s.datePickerCompareFromStartDate;
1659
1660 },
1661
1662 /**
1663 * Util function that gets the end date "compare" from the date picker.
1664 *
1665 * @since 1.8.1
1666 *
1667 */
1668 getEndDateFromCompareFromDatePicker: function () {
1669
1670 return s.datePickerCompareFromEndDate;
1671
1672 },
1673
1674 /**
1675 * Util function that gets the start date "compare" from the date picker.
1676 *
1677 * @since 1.8.1
1678 *
1679 */
1680 getStartDateFromCompareToDatePicker: function () {
1681
1682 return s.datePickerCompareToStartDate;
1683
1684 },
1685
1686 /**
1687 * Util function that gets the end date "compare" from the date picker.
1688 *
1689 * @since 1.8.1
1690 *
1691 */
1692 getEndDateFromCompareToDatePicker: function () {
1693
1694 return s.datePickerCompareToEndDate;
1695
1696 },
1697
1698 /**
1699 * Util function that gets dashboard start date.
1700 *
1701 * @since 1.8.1
1702 *
1703 */
1704 getDashboardStartDate: function () {
1705
1706 return $reports.find('#charitable-dashboard-report-start-date').val();
1707
1708 },
1709
1710 /**
1711 * Util function that gets dashboard end date.
1712 *
1713 * @since 1.8.1
1714 *
1715 */
1716 getDashboardEndDate: function () {
1717
1718 return $reports.find('#charitable-dashboard-report-end-date').val();
1719
1720 },
1721
1722 /**
1723 * Util function that gets dashboard "days" (last 7, 14, 30, etc.)
1724 *
1725 * @since 1.8.1
1726 *
1727 */
1728 getDashboardDays: function () {
1729
1730 return $reports.find('#report-date-range-filter').val();
1731
1732 },
1733
1734 /**
1735 * Util funcion that checks if this is an advanced report page.
1736 *
1737 * @since 1.8.1
1738 *
1739 */
1740 isAdvancedPage: function() {
1741
1742 return ( $('.charitable-advanced-report').length > 0 );
1743
1744 },
1745
1746 /**
1747 * Util funcion that checks if this is a dashboard page.
1748 *
1749 * @since 1.8.1
1750 *
1751 */
1752 isDashboardPage: function() {
1753
1754 return ( $('#charitable-dashboard-report-container').length > 0 );
1755
1756 },
1757
1758 /* utils */
1759
1760 /**
1761 * Util function that decodes HTML entities.
1762 *
1763 * @since 1.8.1
1764 *
1765 */
1766 decodeHtml: function (html) {
1767 var txt = document.createElement("textarea");
1768 txt.innerHTML = html;
1769 return txt.value;
1770 }
1771
1772
1773 };
1774
1775 return app;
1776
1777 }(document, window, jQuery)); // eslint-disable-line no-undef
1778
1779 CharitableAdminReporting.init();
1780