PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.10
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.10
4.4.10 4.4.9.1 4.4.9 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 All 142 releases
← All changes | assets/src/js/admin/list-students-enrolled.js +358 -371 4.3.9.1 → 4.4.10 View file →
@@ -1,371 +1,358 @@
1 -/**
2 - * List Students Enrolled Script
3 - *
4 - * Handles filter and search interactions for the enrolled students table.
5 - * Pagination is handled by loadAJAX.js clickNumberPage (via .page-numbers class).
6 - *
7 - * @since 4.3.3
8 - * @version 1.0.0
9 - */
10 -import * as lpUtils from 'lpAssetsJsPath/utils.js';
11 -
12 -export class ListStudentsEnrolled {
13 - constructor() {
14 - this.instructorId = null;
15 - this.elContainer = null;
16 - this.isRequesting = false;
17 - }
18 -
19 - static selectors = {
20 - elContainer: '#lp-enrolled-students',
21 - elForm: '.lp-enrolled-students-form',
22 - elLPTarget: '.lp-target',
23 - elCourseNameInput: '.lp-enrolled-filter-course-name',
24 - elCourseIdInput: '#lp-enrolled-filter-course-id',
25 - elCourseList: '#lp-enrolled-course-list',
26 - elSearchInput: '.lp-enrolled-search-input',
27 - elStartDateInput: '.lp-enrolled-filter-start-date',
28 - elEndDateInput: '.lp-enrolled-filter-end-date',
29 - elBtnSearch: '.lp-enrolled-btn-search',
30 - elBtnClear: '.lp-enrolled-btn-clear',
31 - };
32 -
33 - init() {
34 - this.elContainer = document.querySelector(
35 - ListStudentsEnrolled.selectors.elContainer
36 - );
37 - if ( ! this.elContainer ) {
38 - return;
39 - }
40 -
41 - const elLPTarget = this.elContainer.querySelector(
42 - ListStudentsEnrolled.selectors.elLPTarget
43 - );
44 - const ajaxHandle = this.getAjaxHandle();
45 - if ( elLPTarget && ajaxHandle ) {
46 - const dataSend = ajaxHandle.getDataSetCurrent( elLPTarget );
47 - if ( dataSend && dataSend.args ) {
48 - this.instructorId = dataSend.args.instructor_id;
49 - }
50 - }
51 -
52 - this.events();
53 - }
54 -
55 - events() {
56 - if ( ListStudentsEnrolled._loadedEvents ) {
57 - return;
58 - }
59 -
60 - ListStudentsEnrolled._loadedEvents = this;
61 -
62 - // Search/Clear button click.
63 - lpUtils.eventHandlers( 'click', [
64 - {
65 - selector: ListStudentsEnrolled.selectors.elBtnSearch,
66 - class: this,
67 - callBack: this.searchStudents.name,
68 - },
69 - {
70 - selector: ListStudentsEnrolled.selectors.elBtnClear,
71 - class: this,
72 - callBack: this.clearFilters.name,
73 - },
74 - ] );
75 -
76 - // Search on Enter key.
77 - lpUtils.eventHandlers( 'keydown', [
78 - {
79 - selector: ListStudentsEnrolled.selectors.elSearchInput,
80 - class: this,
81 - callBack: this.triggerBtnSearch.name,
82 - checkIsEventEnter: true,
83 - },
84 - {
85 - selector: ListStudentsEnrolled.selectors.elCourseNameInput,
86 - class: this,
87 - callBack: this.triggerBtnSearch.name,
88 - checkIsEventEnter: true,
89 - },
90 - {
91 - selector: ListStudentsEnrolled.selectors.elStartDateInput,
92 - class: this,
93 - callBack: this.triggerBtnSearch.name,
94 - checkIsEventEnter: true,
95 - },
96 - {
97 - selector: ListStudentsEnrolled.selectors.elEndDateInput,
98 - class: this,
99 - callBack: this.triggerBtnSearch.name,
100 - checkIsEventEnter: true,
101 - },
102 - ] );
103 -
104 - lpUtils.eventHandlers( 'change', [
105 - {
106 - selector: ListStudentsEnrolled.selectors.elStartDateInput,
107 - class: this,
108 - callBack: this.checkDatesRange.name,
109 - },
110 - {
111 - selector: ListStudentsEnrolled.selectors.elEndDateInput,
112 - class: this,
113 - callBack: this.checkDatesRange.name,
114 - },
115 - ] );
116 - }
117 -
118 - // Click button search.
119 - triggerBtnSearch() {
120 - const buttonSearch = this.elContainer.querySelector(
121 - ListStudentsEnrolled.selectors.elBtnSearch
122 - );
123 - if ( buttonSearch ) {
124 - buttonSearch.click();
125 - }
126 - }
127 -
128 - // Ensure start date is not after end date and vice versa. If invalid, adjust the other date to match.
129 - checkDatesRange( args ) {
130 - const { e } = args;
131 - const elInput = e?.target;
132 - if ( ! elInput ) {
133 - return;
134 - }
135 -
136 - const elForm = elInput.closest( ListStudentsEnrolled.selectors.elForm );
137 - if ( ! elForm ) {
138 - return;
139 - }
140 -
141 - const startDateInput = elForm.querySelector(
142 - ListStudentsEnrolled.selectors.elStartDateInput,
143 - );
144 - const endDateInput = elForm.querySelector(
145 - ListStudentsEnrolled.selectors.elEndDateInput,
146 - );
147 -
148 - if ( elInput === startDateInput ) {
149 - if ( startDateInput.value ) {
150 - endDateInput.min = startDateInput.value;
151 - if ( endDateInput.value && endDateInput.value < startDateInput.value ) {
152 - endDateInput.value = startDateInput.value;
153 - }
154 - } else {
155 - endDateInput.min = '';
156 - }
157 - } else if ( elInput === endDateInput ) {
158 - if ( endDateInput.value ) {
159 - startDateInput.max = endDateInput.value;
160 - if ( startDateInput.value && startDateInput.value > endDateInput.value ) {
161 - startDateInput.value = endDateInput.value;
162 - }
163 - } else {
164 - startDateInput.max = '';
165 - }
166 - }
167 - }
168 -
169 - setButtonLoadingState( btn, isLoading ) {
170 - if ( ! btn ) {
171 - return;
172 - }
173 -
174 - lpUtils.lpSetLoadingEl( btn, isLoading ? 1 : 0 );
175 - btn.disabled = !! isLoading;
176 - }
177 -
178 - getAjaxHandle() {
179 - const ajaxHandle = window.lpAJAXG;
180 - if (
181 - ! ajaxHandle ||
182 - typeof ajaxHandle.getDataSetCurrent !== 'function' ||
183 - typeof ajaxHandle.setDataSetCurrent !== 'function' ||
184 - typeof ajaxHandle.showHideLoading !== 'function' ||
185 - typeof ajaxHandle.fetchAJAX !== 'function'
186 - ) {
187 - return null;
188 - }
189 -
190 - return ajaxHandle;
191 - }
192 -
193 - syncCourseIdFromName( elForm ) {
194 - const courseIdInput = elForm?.querySelector(
195 - ListStudentsEnrolled.selectors.elCourseIdInput
196 - );
197 - if ( ! courseIdInput ) {
198 - return;
199 - }
200 -
201 - courseIdInput.value = '0';
202 -
203 - const courseNameInput = elForm.querySelector(
204 - ListStudentsEnrolled.selectors.elCourseNameInput
205 - );
206 - const datalist = elForm.querySelector(
207 - ListStudentsEnrolled.selectors.elCourseList
208 - );
209 - const courseName = courseNameInput?.value.trim() || '';
210 - if ( ! courseName || ! datalist ) {
211 - return;
212 - }
213 -
214 - const selectedOption = Array.from( datalist.options || [] ).find(
215 - ( option ) => option.value.trim() === courseName
216 - );
217 - if ( selectedOption ) {
218 - courseIdInput.value = selectedOption.dataset.courseId || '0';
219 - }
220 - }
221 -
222 - getFilterArgsFromForm( elForm, dataArgs = {} ) {
223 - this.syncCourseIdFromName( elForm );
224 - return lpUtils.mergeDataWithDatForm( elForm, dataArgs );
225 - }
226 -
227 - /**
228 - * Search students: update args.search, re-fetch.
229 - * @param args
230 - */
231 - searchStudents( args ) {
232 - const { e } = args;
233 - if ( e ) {
234 - e.preventDefault();
235 - }
236 - const btn = args?.target?.closest(
237 - ListStudentsEnrolled.selectors.elBtnSearch
238 - );
239 - if ( btn ) {
240 - if (
241 - this.isRequesting ||
242 - btn.classList.contains( 'loading' ) ||
243 - btn.disabled
244 - ) {
245 - return;
246 - }
247 - } else if ( this.isRequesting ) {
248 - return;
249 - }
250 -
251 - const elForm = btn.closest( ListStudentsEnrolled.selectors.elForm );
252 - const elLPTarget = this.elContainer.querySelector(
253 - ListStudentsEnrolled.selectors.elLPTarget
254 - );
255 - if ( ! elLPTarget || ! elForm ) {
256 - return;
257 - }
258 -
259 - const ajaxHandle = this.getAjaxHandle();
260 - if ( ! ajaxHandle ) {
261 - return;
262 - }
263 -
264 - this.setButtonLoadingState( btn, true );
265 -
266 - const dataSend = ajaxHandle.getDataSetCurrent( elLPTarget );
267 - dataSend.args = this.getFilterArgsFromForm(
268 - elForm,
269 - dataSend.args || {}
270 - );
271 - dataSend.args.paged = 1;
272 - ajaxHandle.setDataSetCurrent( elLPTarget, dataSend );
273 -
274 - this.reloadContent( elLPTarget, dataSend, btn );
275 - }
276 -
277 - /**
278 - * Clear all filters and reload default data.
279 - * @param args
280 - */
281 - clearFilters( args ) {
282 - const { e } = args;
283 - if ( e ) {
284 - e.preventDefault();
285 - }
286 - const btn = args?.target?.closest(
287 - ListStudentsEnrolled.selectors.elBtnClear
288 - );
289 - if ( btn ) {
290 - if (
291 - this.isRequesting ||
292 - btn.classList.contains( 'loading' ) ||
293 - btn.disabled
294 - ) {
295 - return;
296 - }
297 - } else if ( this.isRequesting ) {
298 - return;
299 - }
300 -
301 - const elForm = btn.closest( ListStudentsEnrolled.selectors.elForm );
302 - const elLPTarget = this.elContainer.querySelector(
303 - ListStudentsEnrolled.selectors.elLPTarget
304 - );
305 - if ( ! elLPTarget || ! elForm ) {
306 - return;
307 - }
308 -
309 - const ajaxHandle = this.getAjaxHandle();
310 - if ( ! ajaxHandle ) {
311 - return;
312 - }
313 -
314 - this.setButtonLoadingState( btn, true );
315 -
316 - elForm.reset();
317 - this.syncCourseIdFromName( elForm );
318 -
319 - const dataSend = ajaxHandle.getDataSetCurrent( elLPTarget );
320 - dataSend.args = lpUtils.mergeDataWithDatForm(
321 - elForm,
322 - dataSend.args || {}
323 - );
324 - dataSend.args.paged = 1;
325 - ajaxHandle.setDataSetCurrent( elLPTarget, dataSend );
326 -
327 - this.reloadContent( elLPTarget, dataSend, btn );
328 - }
329 -
330 - /**
331 - * Shared reload helper: loading indicator + AJAX fetch.
332 - * @param elLPTarget
333 - * @param dataSend
334 - * @param btn
335 - */
336 - reloadContent( elLPTarget, dataSend, btn = null ) {
337 - const ajaxHandle = this.getAjaxHandle();
338 - if ( ! ajaxHandle ) {
339 - this.isRequesting = false;
340 - this.setButtonLoadingState( btn, false );
341 - return;
342 - }
343 -
344 - this.isRequesting = true;
345 - ajaxHandle.showHideLoading( elLPTarget, 1 );
346 -
347 - const callBack = {
348 - success: ( response ) => {
349 - const { status, data } = response;
350 - if ( 'success' === status ) {
351 - elLPTarget.innerHTML = data.content;
352 - }
353 - },
354 - error: ( error ) => console.error( error ),
355 - completed: () => {
356 - this.isRequesting = false;
357 - ajaxHandle.showHideLoading( elLPTarget, 0 );
358 - this.setButtonLoadingState( btn, false );
359 - },
360 - };
361 -
362 - ajaxHandle.fetchAJAX( dataSend, callBack );
363 - }
364 -}
365 -
366 -// Auto-initialize when DOM is available (for standalone page load).
367 -const listStudentsEnrolled = new ListStudentsEnrolled();
368 -
369 -lpUtils.lpOnElementReady( ListStudentsEnrolled.selectors.elContainer, () => {
370 - listStudentsEnrolled.init();
371 -} );
1 +/**
2 + * List Students Enrolled Script
3 + *
4 + * Handles filter and search interactions for the enrolled students table.
5 + * Pagination is handled by loadAJAX.js clickNumberPage (via .page-numbers class).
6 + *
7 + * @since 4.3.3
8 + * @version 1.0.0
9 + */
10 +import * as lpUtils from 'lpAssetsJsPath/utils.js';
11 +
12 +export class ListStudentsEnrolled {
13 + constructor() {
14 + this.instructorId = null;
15 + this.elContainer = null;
16 + }
17 +
18 + static selectors = {
19 + elContainer: '.lp-enrolled-students',
20 + elLayout: '.lp-students-enrolled-layout',
21 + elForm: '.lp-enrolled-students-form',
22 + elLPTarget: '.lp-target',
23 + elCourseNameInput: '.lp-enrolled-filter-course-name',
24 + elCourseIdInput: '#lp-enrolled-filter-course-id',
25 + elCourseList: '#lp-enrolled-course-list',
26 + elSearchInput: '.lp-enrolled-search-input',
27 + elStartDateInput: '.lp-enrolled-filter-start-date',
28 + elEndDateInput: '.lp-enrolled-filter-end-date',
29 + elBtnSearch: '.lp-enrolled-btn-search',
30 + elBtnClear: '.lp-enrolled-btn-clear',
31 + };
32 +
33 + init() {
34 + this.elContainer = document.querySelector(
35 + ListStudentsEnrolled.selectors.elContainer
36 + );
37 + if ( ! this.elContainer ) {
38 + return;
39 + }
40 +
41 + const elLPTarget = this.elContainer.querySelector(
42 + ListStudentsEnrolled.selectors.elLPTarget
43 + );
44 + const ajaxHandle = this.getAjaxHandle();
45 + if ( elLPTarget && ajaxHandle ) {
46 + const dataSend = ajaxHandle.getDataSetCurrent( elLPTarget );
47 + if ( dataSend && dataSend.args ) {
48 + this.instructorId = dataSend.args.instructor_id;
49 + }
50 + }
51 +
52 + this.events();
53 + }
54 +
55 + events() {
56 + if ( ListStudentsEnrolled._loadedEvents ) {
57 + return;
58 + }
59 +
60 + ListStudentsEnrolled._loadedEvents = this;
61 +
62 + // Search/Clear button click.
63 + lpUtils.eventHandlers( 'click', [
64 + {
65 + selector: ListStudentsEnrolled.selectors.elBtnSearch,
66 + callBack: this.searchStudents.bind( this ),
67 + },
68 + {
69 + selector: ListStudentsEnrolled.selectors.elBtnClear,
70 + callBack: this.clearFilters.bind( this ),
71 + },
72 + ] );
73 +
74 + // Search on Enter key.
75 + lpUtils.eventHandlers( 'keydown', [
76 + {
77 + selector: ListStudentsEnrolled.selectors.elSearchInput,
78 + callBack: this.triggerBtnSearch.bind( this ),
79 + checkIsEventEnter: true,
80 + },
81 + {
82 + selector: ListStudentsEnrolled.selectors.elCourseNameInput,
83 + callBack: this.triggerBtnSearch.bind( this ),
84 + checkIsEventEnter: true,
85 + },
86 + {
87 + selector: ListStudentsEnrolled.selectors.elStartDateInput,
88 + callBack: this.triggerBtnSearch.bind( this ),
89 + checkIsEventEnter: true,
90 + },
91 + {
92 + selector: ListStudentsEnrolled.selectors.elEndDateInput,
93 + callBack: this.triggerBtnSearch.bind( this ),
94 + checkIsEventEnter: true,
95 + },
96 + ] );
97 +
98 + lpUtils.eventHandlers( 'change', [
99 + {
100 + selector: ListStudentsEnrolled.selectors.elStartDateInput,
101 + callBack: this.checkDatesRange.bind( this ),
102 + },
103 + {
104 + selector: ListStudentsEnrolled.selectors.elEndDateInput,
105 + callBack: this.checkDatesRange.bind( this ),
106 + },
107 + ] );
108 + }
109 +
110 + // Click button search.
111 + triggerBtnSearch() {
112 + const buttonSearch = this.elContainer.querySelector(
113 + ListStudentsEnrolled.selectors.elBtnSearch
114 + );
115 + if ( buttonSearch ) {
116 + buttonSearch.click();
117 + }
118 + }
119 +
120 + // Ensure start date is not after end date and vice versa. If invalid, adjust the other date to match.
121 + checkDatesRange( args ) {
122 + const { e } = args;
123 + const elInput = e?.target;
124 + if ( ! elInput ) {
125 + return;
126 + }
127 +
128 + const elForm = elInput.closest( ListStudentsEnrolled.selectors.elForm );
129 + if ( ! elForm ) {
130 + return;
131 + }
132 +
133 + const startDateInput = elForm.querySelector(
134 + ListStudentsEnrolled.selectors.elStartDateInput
135 + );
136 + const endDateInput = elForm.querySelector(
137 + ListStudentsEnrolled.selectors.elEndDateInput
138 + );
139 +
140 + if ( elInput === startDateInput ) {
141 + if ( startDateInput.value ) {
142 + endDateInput.min = startDateInput.value;
143 + if (
144 + endDateInput.value &&
145 + endDateInput.value < startDateInput.value
146 + ) {
147 + endDateInput.value = startDateInput.value;
148 + }
149 + } else {
150 + endDateInput.min = '';
151 + }
152 + } else if ( elInput === endDateInput ) {
153 + if ( endDateInput.value ) {
154 + startDateInput.max = endDateInput.value;
155 + if (
156 + startDateInput.value &&
157 + startDateInput.value > endDateInput.value
158 + ) {
159 + startDateInput.value = endDateInput.value;
160 + }
161 + } else {
162 + startDateInput.max = '';
163 + }
164 + }
165 + }
166 +
167 + setButtonLoadingState( btn, isLoading ) {
168 + if ( ! btn ) {
169 + return;
170 + }
171 +
172 + lpUtils.lpSetLoadingEl( btn, isLoading ? 1 : 0 );
173 + //btn.disabled = !! isLoading;
174 + }
175 +
176 + getAjaxHandle() {
177 + const ajaxHandle = window.lpAJAXG;
178 + if (
179 + ! ajaxHandle ||
180 + typeof ajaxHandle.getDataSetCurrent !== 'function' ||
181 + typeof ajaxHandle.setDataSetCurrent !== 'function' ||
182 + typeof ajaxHandle.showHideLoading !== 'function' ||
183 + typeof ajaxHandle.fetchAJAX !== 'function'
184 + ) {
185 + return null;
186 + }
187 +
188 + return ajaxHandle;
189 + }
190 +
191 + syncCourseIdFromName( elForm ) {
192 + const courseIdInput = elForm?.querySelector(
193 + ListStudentsEnrolled.selectors.elCourseIdInput
194 + );
195 + if ( ! courseIdInput ) {
196 + return;
197 + }
198 +
199 + courseIdInput.value = '0';
200 +
201 + const courseNameInput = elForm.querySelector(
202 + ListStudentsEnrolled.selectors.elCourseNameInput
203 + );
204 + const datalist = elForm.querySelector(
205 + ListStudentsEnrolled.selectors.elCourseList
206 + );
207 + const courseName = courseNameInput?.value.trim() || '';
208 + if ( ! courseName || ! datalist ) {
209 + return;
210 + }
211 +
212 + const selectedOption = Array.from( datalist.options || [] ).find(
213 + ( option ) => option.value.trim() === courseName
214 + );
215 + if ( selectedOption ) {
216 + courseIdInput.value = selectedOption.dataset.courseId || '0';
217 + }
218 + }
219 +
220 + getFilterArgsFromForm( elForm, dataArgs = {} ) {
221 + this.syncCourseIdFromName( elForm );
222 + return lpUtils.mergeDataWithDatForm( elForm, dataArgs );
223 + }
224 +
225 + /**
226 + * Search students: update args.search, re-fetch.
227 + * @param args
228 + */
229 + searchStudents( args ) {
230 + const { e } = args;
231 + if ( e ) {
232 + e.preventDefault();
233 + }
234 + const btn = args?.target?.closest(
235 + ListStudentsEnrolled.selectors.elBtnSearch
236 + );
237 +
238 + if ( ! btn ) {
239 + return;
240 + }
241 +
242 + const elForm = btn.closest( ListStudentsEnrolled.selectors.elForm );
243 + const elLayout = elForm.closest(
244 + ListStudentsEnrolled.selectors.elLayout
245 + );
246 + const elLPTarget = elLayout.querySelector(
247 + ListStudentsEnrolled.selectors.elLPTarget
248 + );
249 + if ( ! elLPTarget || ! elForm ) {
250 + return;
251 + }
252 +
253 + const ajaxHandle = this.getAjaxHandle();
254 + if ( ! ajaxHandle ) {
255 + return;
256 + }
257 +
258 + this.setButtonLoadingState( btn, true );
259 +
260 + const dataSend = ajaxHandle.getDataSetCurrent( elLPTarget );
261 + dataSend.args = this.getFilterArgsFromForm(
262 + elForm,
263 + dataSend.args || {}
264 + );
265 + dataSend.args.paged = 1;
266 + ajaxHandle.setDataSetCurrent( elLPTarget, dataSend );
267 +
268 + this.reloadContent( elLPTarget, dataSend, btn );
269 + }
270 +
271 + /**
272 + * Clear all filters and reload default data.
273 + * @param args
274 + */
275 + clearFilters( args ) {
276 + const { e } = args;
277 + if ( e ) {
278 + e.preventDefault();
279 + }
280 + const btn = args?.target?.closest(
281 + ListStudentsEnrolled.selectors.elBtnClear
282 + );
283 +
284 + if ( ! btn ) {
285 + return;
286 + }
287 +
288 + const elForm = btn.closest( ListStudentsEnrolled.selectors.elForm );
289 + const elLayout = elForm.closest(
290 + ListStudentsEnrolled.selectors.elLayout
291 + );
292 + const elLPTarget = elLayout.querySelector(
293 + ListStudentsEnrolled.selectors.elLPTarget
294 + );
295 + if ( ! elLPTarget || ! elForm ) {
296 + return;
297 + }
298 +
299 + const ajaxHandle = this.getAjaxHandle();
300 + if ( ! ajaxHandle ) {
301 + return;
302 + }
303 +
304 + this.setButtonLoadingState( btn, true );
305 +
306 + elForm.reset();
307 + this.syncCourseIdFromName( elForm );
308 +
309 + const dataSend = ajaxHandle.getDataSetCurrent( elLPTarget );
310 + dataSend.args = lpUtils.mergeDataWithDatForm(
311 + elForm,
312 + dataSend.args || {}
313 + );
314 + dataSend.args.paged = 1;
315 + ajaxHandle.setDataSetCurrent( elLPTarget, dataSend );
316 +
317 + this.reloadContent( elLPTarget, dataSend, btn );
318 + }
319 +
320 + /**
321 + * Shared reload helper: loading indicator + AJAX fetch.
322 + * @param elLPTarget
323 + * @param dataSend
324 + * @param btn
325 + */
326 + reloadContent( elLPTarget, dataSend, btn = null ) {
327 + const ajaxHandle = this.getAjaxHandle();
328 + if ( ! ajaxHandle ) {
329 + this.setButtonLoadingState( btn, false );
330 + return;
331 + }
332 +
333 + ajaxHandle.showHideLoading( elLPTarget, 1 );
334 +
335 + const callBack = {
336 + success: ( response ) => {
337 + const { status, data } = response;
338 + if ( 'success' === status ) {
339 + elLPTarget.innerHTML = data.content;
340 + }
341 + },
342 + error: ( error ) => console.error( error ),
343 + completed: () => {
344 + ajaxHandle.showHideLoading( elLPTarget, 0 );
345 + this.setButtonLoadingState( btn, false );
346 + },
347 + };
348 +
349 + ajaxHandle.fetchAJAX( dataSend, callBack );
350 + }
351 +}
352 +
353 +// Auto-initialize when DOM is available (for standalone page load).
354 +const listStudentsEnrolled = new ListStudentsEnrolled();
355 +
356 +lpUtils.lpOnElementReady( ListStudentsEnrolled.selectors.elContainer, () => {
357 + listStudentsEnrolled.init();
358 +} );