PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / assets / src / js / admin / statistics / report-modal.js

report-modal.js in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.8, at assets/src/js/admin/statistics/report-modal.js

300 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Report popup controller — SweetAlert2 shell over a server-rendered table.
3 *
4 * The table is built in PHP ( AdminStatisticsReportTable ) via TableListTemplate
5 * and delivered through TemplateAJAX: open() injects the popup body, points the
6 * .lp-target at the requested report + current filters, and triggers loadAJAX to
7 * fetch it. Pagination is handled by loadAJAX.js ( .page-numbers ). Search
8 * re-queries the server ( debounced, resets to page 1 ); export asks the server
9 * for the full CSV and downloads it.
10 *
11 * @since 4.4.2
12 * @version 3.0.0
13 */
14
15 import SweetAlert from 'sweetalert2';
16 import * as lpUtils from 'lpAssetsJsPath/utils.js';
17 import { lpStatsState } from './state.js';
18 import { getStatsI18n } from './api.js';
19
20 export class LpStatsReportModal {
21 static selectors = {
22 template: '#lp-tmpl-stats-report-modal',
23 elContainer: '.lp-stats-report-modal',
24 elSearch: '.lp-stats-report-modal__search',
25 elExport: '.lp-stats-report-modal__export',
26 elTarget: '.lp-target',
27 };
28
29 constructor() {
30 this.title = '';
31 this.tableId = '';
32 }
33
34 init() {
35 this.events();
36 }
37
38 events() {
39 if ( LpStatsReportModal._loadedEvents ) {
40 return;
41 }
42 LpStatsReportModal._loadedEvents = this;
43
44 // Debounced ONCE here — never create a debounce inside a handler.
45 this.debouncedSearch = lpUtils.debounce( () => this.applySearch(), 400 );
46
47 lpUtils.eventHandlers( 'click', [
48 {
49 selector: LpStatsReportModal.selectors.elExport,
50 class: this,
51 callBack: this.exportCsv.name,
52 },
53 ] );
54
55 lpUtils.eventHandlers( 'input', [
56 {
57 selector: LpStatsReportModal.selectors.elSearch,
58 class: this,
59 callBack: this.onSearchInput.name,
60 },
61 ] );
62 }
63
64 /**
65 * @return {Object|null} window.lpAJAXG when it exposes the API we need.
66 */
67 getAjaxHandle() {
68 const handle = window.lpAJAXG;
69 if (
70 ! handle ||
71 'function' !== typeof handle.getDataSetCurrent ||
72 'function' !== typeof handle.setDataSetCurrent ||
73 'function' !== typeof handle.fetchAJAX ||
74 'function' !== typeof handle.showHideLoading
75 ) {
76 return null;
77 }
78
79 return handle;
80 }
81
82 getModalPopup() {
83 return SweetAlert.getPopup ? SweetAlert.getPopup() : null;
84 }
85
86 getModalContent() {
87 const popup = this.getModalPopup();
88 if ( ! popup ) {
89 return null;
90 }
91
92 return popup.querySelector( LpStatsReportModal.selectors.elContainer );
93 }
94
95 getTarget() {
96 const content = this.getModalContent();
97 return content
98 ? content.querySelector( LpStatsReportModal.selectors.elTarget )
99 : null;
100 }
101
102 getModalHtml() {
103 const template = document.querySelector(
104 LpStatsReportModal.selectors.template
105 );
106
107 return template ? template.innerHTML : '';
108 }
109
110 isOpen() {
111 return !! this.getModalContent();
112 }
113
114 /**
115 * @param {Object} report { report, title, tableId?, orderStatus? }
116 * - report: server report slug ( e.g. 'top_courses' ).
117 * - orderStatus: cancelled/failed deep-link for the exceptions report.
118 */
119 open( report = {} ) {
120 const modalHtml = this.getModalHtml();
121 if ( ! modalHtml || ! report.report ) {
122 return;
123 }
124
125 this.init();
126
127 this.title = report.title || '';
128 this.tableId = report.tableId || report.report;
129
130 SweetAlert.fire( {
131 title: this.title,
132 html: modalHtml,
133 // Large by default; the custom class lets the SCSS push it (near) full size.
134 width: '100%',
135 customClass: { popup: 'lp-stats-report-popup' },
136 showConfirmButton: false,
137 showCloseButton: true,
138 didOpen: () => this.loadReport( report ),
139 } );
140 }
141
142 close() {
143 SweetAlert.close();
144 }
145
146 /**
147 * Seed the .lp-target with report + current filters and fetch page 1.
148 *
149 * @param {Object} report
150 */
151 loadReport( report ) {
152 const target = this.getTarget();
153 const handle = this.getAjaxHandle();
154 if ( ! target || ! handle ) {
155 if ( target ) {
156 target.innerHTML = getStatsI18n( 'loadError', 'Request failed.' );
157 }
158 return;
159 }
160
161 const dataSend = handle.getDataSetCurrent( target );
162 dataSend.args = {
163 ...( dataSend.args || {} ),
164 ...lpStatsState.get(),
165 report: report.report,
166 search: '',
167 paged: 1,
168 // Report-specific args ( e.g. instructor_id ) win over the global filters.
169 ...( report.args || {} ),
170 };
171 if ( report.orderStatus ) {
172 dataSend.args.order_status = report.orderStatus;
173 }
174 handle.setDataSetCurrent( target, dataSend );
175
176 this.reloadTarget( target, dataSend );
177 }
178
179 onSearchInput() {
180 this.debouncedSearch();
181 }
182
183 applySearch() {
184 const content = this.getModalContent();
185 const target = this.getTarget();
186 const handle = this.getAjaxHandle();
187 if ( ! content || ! target || ! handle ) {
188 return;
189 }
190
191 const elSearch = content.querySelector(
192 LpStatsReportModal.selectors.elSearch
193 );
194 const dataSend = handle.getDataSetCurrent( target );
195 dataSend.args = dataSend.args || {};
196 dataSend.args.search = ( elSearch?.value || '' ).trim();
197 dataSend.args.paged = 1;
198 handle.setDataSetCurrent( target, dataSend );
199
200 this.reloadTarget( target, dataSend );
201 }
202
203 /**
204 * Loading indicator + AJAX fetch, swapping the target's innerHTML.
205 *
206 * @param {Element} target
207 * @param {Object} dataSend
208 */
209 reloadTarget( target, dataSend ) {
210 const handle = this.getAjaxHandle();
211 if ( ! handle ) {
212 return;
213 }
214
215 handle.showHideLoading( target, 1 );
216
217 handle.fetchAJAX( dataSend, {
218 success: ( response ) => {
219 const { status, message, data } = response;
220 if ( 'success' === status ) {
221 target.innerHTML = data.content || '';
222 } else {
223 target.innerHTML =
224 message || getStatsI18n( 'loadError', 'Request failed.' );
225 }
226 },
227 error: ( err ) => {
228 // eslint-disable-next-line no-console
229 console.error( 'LP Statistics report:', err );
230 },
231 completed: () => handle.showHideLoading( target, 0 ),
232 } );
233 }
234
235 /**
236 * Ask the server for the full ( capped ) CSV and download it.
237 */
238 exportCsv( args ) {
239 const content = this.getModalContent();
240 const target = this.getTarget();
241 const handle = this.getAjaxHandle();
242 if ( ! content || ! target || ! handle ) {
243 return;
244 }
245
246 const btn = args?.target?.closest( LpStatsReportModal.selectors.elExport );
247 if ( ! btn || btn.classList.contains( 'loading' ) ) {
248 return;
249 }
250
251 // Clone the current request but hit the CSV callback.
252 const current = handle.getDataSetCurrent( target );
253 const dataSend = {
254 ...current,
255 args: { ...( current.args || {} ) },
256 callback: { ...( current.callback || {} ), method: 'render_report_csv' },
257 };
258
259 lpUtils.lpSetLoadingEl( btn, 1 );
260
261 handle.fetchAJAX( dataSend, {
262 success: ( response ) => {
263 const { status, data } = response;
264 if ( 'success' === status && data && data.csv ) {
265 this.download(
266 data.filename || 'learnpress-report.csv',
267 data.csv
268 );
269 }
270 },
271 error: ( err ) => {
272 // eslint-disable-next-line no-console
273 console.error( 'LP Statistics export:', err );
274 },
275 completed: () => lpUtils.lpSetLoadingEl( btn, 0 ),
276 } );
277 }
278
279 /**
280 * @param {string} filename
281 * @param {string} csv
282 */
283 download( filename, csv ) {
284 // BOM keeps Excel reading UTF-8 (Vietnamese titles etc.).
285 const blob = new Blob( [ '\u{FEFF}' + csv ], {
286 type: 'text/csv;charset=utf-8;',
287 } );
288 const url = URL.createObjectURL( blob );
289 const link = document.createElement( 'a' );
290 link.href = url;
291 link.download = filename;
292 document.body.appendChild( link );
293 link.click();
294 document.body.removeChild( link );
295 URL.revokeObjectURL( url );
296 }
297 }
298
299 export const lpStatsReportModal = new LpStatsReportModal();
300