PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.2
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.2
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 / frontend / course-builder / builder-lesson / builder-tab-lesson.js

builder-tab-lesson.js in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.2, at assets/src/js/frontend/course-builder/builder-lesson/builder-tab-lesson.js

558 lines 14.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import * as lpUtils from 'lpAssetsJsPath/utils.js';
2 import * as lpToastify from 'lpAssetsJsPath/lpToastify.js';
3 import SweetAlert from 'sweetalert2';
4 import { SWAL_ICON_DELETE, SWAL_ICON_DUPLICATE, SWAL_ICON_TRASH_DRAFT } from '../swal-icons.js';
5
6 export class BuilderTabLesson {
7 constructor() {
8 this.init();
9 }
10
11 static selectors = {
12 elLessonItem: '.lesson-item',
13 elLessonExpandedItems: '.lesson-action-expanded__items',
14 elLessonDuplicate: '.lesson-action-expanded__duplicate',
15 elLessonTrash: '.lesson-action-expanded__trash',
16 elLessonRestore: '.lesson-action-expanded__restore',
17 elLessonPublish: '.lesson-action-expanded__publish',
18 elLessonDelete: '.lesson-action-expanded__delete',
19 elLessonActionExpanded: '.lesson-action-expanded',
20 elLessonStatus: '.lesson-status',
21 elLessonPreview: '.lesson__preview.lp-btn-set-preview-item',
22 };
23
24 init() {
25 this.events();
26 }
27
28 events() {
29 if ( BuilderTabLesson._loadedEvents ) {
30 return;
31 }
32 BuilderTabLesson._loadedEvents = true;
33
34 lpUtils.eventHandlers( 'click', [
35 {
36 selector: BuilderTabLesson.selectors.elLessonDuplicate,
37 class: this,
38 callBack: this.duplicateLesson.name,
39 },
40 {
41 selector: BuilderTabLesson.selectors.elLessonTrash,
42 class: this,
43 callBack: this.trashLesson.name,
44 },
45 {
46 selector: BuilderTabLesson.selectors.elLessonPublish,
47 class: this,
48 callBack: this.publishLesson.name,
49 },
50 {
51 selector: BuilderTabLesson.selectors.elLessonRestore,
52 class: this,
53 callBack: this.restoreLesson.name,
54 },
55 {
56 selector: BuilderTabLesson.selectors.elLessonDelete,
57 class: this,
58 callBack: this.deleteLesson.name,
59 },
60 {
61 selector: BuilderTabLesson.selectors.elLessonActionExpanded,
62 class: this,
63 callBack: this.toggleExpandedAction.name,
64 },
65 {
66 selector: BuilderTabLesson.selectors.elLessonPreview,
67 class: this,
68 callBack: this.toggleLessonPreview.name,
69 },
70 ] );
71
72 document.addEventListener( 'click', ( e ) => {
73 if ( ! e.target.closest( BuilderTabLesson.selectors.elLessonActionExpanded ) ) {
74 this.closeAllExpanded();
75 }
76 } );
77
78 document.addEventListener( 'lp-builder-popup-saved', ( e ) => {
79 const { type, id, data } = e.detail;
80 if ( type !== 'lesson' || ! data?.list_item_html ) return;
81 const elItem = document.querySelector( `.lesson-item[data-lesson-id="${ id }"]` );
82 if ( elItem ) {
83 this.replaceItemHtml( elItem.closest( '.lesson' ), data.list_item_html );
84 }
85 } );
86
87 document.addEventListener( 'lp-builder-popup-trashed', ( e ) => {
88 const { type, id, data } = e.detail;
89 if ( type !== 'lesson' || ! data?.html ) return;
90 const elItem = document.querySelector( `.lesson-item[data-lesson-id="${ id }"]` );
91 if ( elItem ) {
92 this.replaceItemHtml( elItem.closest( '.lesson' ), data.html );
93 }
94 } );
95 }
96
97 duplicateLesson( args ) {
98 const { target } = args;
99 const elLessonDuplicate = target.closest( BuilderTabLesson.selectors.elLessonDuplicate );
100 const elLessonItem = elLessonDuplicate.closest( BuilderTabLesson.selectors.elLessonItem );
101
102 if ( ! elLessonItem ) {
103 return;
104 }
105
106 const elActionExpanded = elLessonItem.querySelector(
107 BuilderTabLesson.selectors.elLessonActionExpanded
108 );
109 const lessonId = elLessonItem.dataset.lessonId || '';
110
111 SweetAlert.fire( {
112 title: elLessonDuplicate.dataset.title || 'Duplicate Lesson',
113 text: elLessonDuplicate.dataset.content || 'Are you sure you want to duplicate this lesson?',
114 iconHtml: SWAL_ICON_DUPLICATE,
115 customClass: { icon: 'lp-cb-swal-icon-html' },
116 showCloseButton: true,
117 showCancelButton: true,
118 cancelButtonText: lpData.i18n.cancel,
119 confirmButtonText: lpData.i18n.yes,
120 reverseButtons: true,
121 } ).then( ( result ) => {
122 if ( result.isConfirmed ) {
123 lpUtils.lpSetLoadingEl( elActionExpanded, 1 );
124
125 const dataSend = {
126 action: 'duplicate_lesson',
127 args: {
128 id_url: 'duplicate-lesson',
129 },
130 lesson_id: lessonId,
131 };
132
133 const callBack = {
134 success: ( response ) => {
135 const { status, message, data } = response;
136 lpToastify.show( message || 'Duplicated successfully!', status );
137
138 if ( data?.html ) {
139 const elLesson = elLessonDuplicate.closest( '.lesson' );
140 const elLessonList = elLesson.closest( '.lessons-list' ) || elLesson.parentElement;
141
142 if ( elLessonList ) {
143 elLessonList.insertAdjacentHTML( 'afterbegin', data.html );
144 const newLesson = elLessonList.firstElementChild;
145
146 if ( newLesson ) {
147 newLesson.scrollIntoView( {
148 behavior: 'smooth',
149 block: 'nearest',
150 } );
151 newLesson.classList.add( 'highlight-new-lesson' );
152 setTimeout( () => {
153 newLesson.classList.remove( 'highlight-new-lesson' );
154 }, 1500 );
155 }
156 }
157 }
158 },
159 error: ( error ) => {
160 lpToastify.show( error.message || error, 'error' );
161 },
162 completed: () => {
163 lpUtils.lpSetLoadingEl( elActionExpanded, 0 );
164 },
165 };
166
167 window.lpAJAXG.fetchAJAX( dataSend, callBack );
168 }
169 } );
170 }
171
172 trashLesson( args ) {
173 const { target } = args;
174 const elLessonTrash = target.closest( BuilderTabLesson.selectors.elLessonTrash );
175 const elLessonItem = elLessonTrash.closest( BuilderTabLesson.selectors.elLessonItem );
176
177 if ( ! elLessonItem ) {
178 return;
179 }
180
181 const elActionExpanded = elLessonItem.querySelector(
182 BuilderTabLesson.selectors.elLessonActionExpanded
183 );
184 const lessonId = elLessonItem.dataset.lessonId || '';
185
186 SweetAlert.fire( {
187 title: elLessonTrash.dataset.title || 'Trash Lesson',
188 text:
189 elLessonTrash.dataset.content ||
190 'Are you sure? Moving it to the trash will cause this item to be removed from the course.',
191 iconHtml: SWAL_ICON_TRASH_DRAFT,
192 customClass: { icon: 'lp-cb-swal-icon-html' },
193 showCloseButton: true,
194 showCancelButton: true,
195 cancelButtonText: lpData.i18n.cancel,
196 confirmButtonText: lpData.i18n.yes,
197 reverseButtons: true,
198 } ).then( ( result ) => {
199 if ( result.isConfirmed ) {
200 lpUtils.lpSetLoadingEl( elActionExpanded, 1 );
201
202 const dataSend = {
203 action: 'move_trash_lesson',
204 args: {
205 id_url: 'move-trash-lesson',
206 },
207 lesson_id: lessonId,
208 };
209
210 const callBack = {
211 success: ( response ) => {
212 const { status, message, data } = response;
213 lpToastify.show( message, status );
214
215 if ( data?.html ) {
216 this.replaceItemHtml( elLessonTrash.closest( '.lesson' ), data.html );
217 }
218 },
219 error: ( error ) => {
220 lpToastify.show( error.message || error, 'error' );
221 },
222 completed: () => {
223 lpUtils.lpSetLoadingEl( elActionExpanded, 0 );
224 },
225 };
226
227 window.lpAJAXG.fetchAJAX( dataSend, callBack );
228 }
229 } );
230 }
231
232 publishLesson( args ) {
233 const { target } = args;
234 const elLessonPublish = target.closest( BuilderTabLesson.selectors.elLessonPublish );
235 const elLessonItem = elLessonPublish.closest( BuilderTabLesson.selectors.elLessonItem );
236
237 if ( ! elLessonItem ) {
238 return;
239 }
240
241 const elActionExpanded = elLessonItem.querySelector(
242 BuilderTabLesson.selectors.elLessonActionExpanded
243 );
244 lpUtils.lpSetLoadingEl( elActionExpanded, 1 );
245
246 const lessonId = elLessonItem.dataset.lessonId || '';
247
248 const dataSend = {
249 action: 'move_trash_lesson',
250 args: {
251 id_url: 'move-trash-lesson',
252 },
253 lesson_id: lessonId,
254 status: 'publish',
255 };
256
257 const callBack = {
258 success: ( response ) => {
259 const { status, message, data } = response;
260 lpToastify.show( message, status );
261 if ( data?.html ) {
262 this.replaceItemHtml( elLessonPublish.closest( '.lesson' ), data.html );
263 }
264 },
265 error: ( error ) => {
266 lpToastify.show( error.message || error, 'error' );
267 },
268 completed: () => {
269 lpUtils.lpSetLoadingEl( elActionExpanded, 0 );
270 },
271 };
272
273 window.lpAJAXG.fetchAJAX( dataSend, callBack );
274 }
275
276 restoreLesson( args ) {
277 const { target } = args;
278 const elLessonRestore = target.closest( BuilderTabLesson.selectors.elLessonRestore );
279 const elLessonItem = elLessonRestore.closest( BuilderTabLesson.selectors.elLessonItem );
280
281 if ( ! elLessonItem ) {
282 return;
283 }
284
285 const elActionExpanded = elLessonItem.querySelector(
286 BuilderTabLesson.selectors.elLessonActionExpanded
287 );
288 lpUtils.lpSetLoadingEl( elActionExpanded, 1 );
289
290 const lessonId = elLessonItem.dataset.lessonId || '';
291
292 const dataSend = {
293 action: 'move_trash_lesson',
294 args: {
295 id_url: 'move-trash-lesson',
296 },
297 lesson_id: lessonId,
298 status: 'draft',
299 };
300
301 const callBack = {
302 success: ( response ) => {
303 const { status, message, data } = response;
304 lpToastify.show( message, status );
305 if ( data?.html ) {
306 this.replaceItemHtml( elLessonRestore.closest( '.lesson' ), data.html );
307 }
308 },
309 error: ( error ) => {
310 lpToastify.show( error.message || error, 'error' );
311 },
312 completed: () => {
313 lpUtils.lpSetLoadingEl( elActionExpanded, 0 );
314 },
315 };
316
317 window.lpAJAXG.fetchAJAX( dataSend, callBack );
318 }
319
320 deleteLesson( args ) {
321 const { target } = args;
322 const elLessonDelete = target.closest( BuilderTabLesson.selectors.elLessonDelete );
323 const elLessonItem = elLessonDelete.closest( BuilderTabLesson.selectors.elLessonItem );
324
325 if ( ! elLessonItem ) {
326 return;
327 }
328
329 const elActionExpanded = elLessonItem.querySelector(
330 BuilderTabLesson.selectors.elLessonActionExpanded
331 );
332 const lessonId = elLessonItem.dataset.lessonId || '';
333
334 if ( ! lessonId ) {
335 return;
336 }
337
338 SweetAlert.fire( {
339 title: elLessonDelete.dataset.title || 'Delete Lesson',
340 text:
341 elLessonDelete.dataset.content ||
342 'Are you sure you want to permanently delete this lesson?',
343 iconHtml: SWAL_ICON_DELETE,
344 customClass: { icon: 'lp-cb-swal-icon-html' },
345 showCloseButton: true,
346 showCancelButton: true,
347 cancelButtonText: lpData.i18n.cancel,
348 confirmButtonText: lpData.i18n.yes,
349 reverseButtons: true,
350 } ).then( ( result ) => {
351 if ( result.isConfirmed ) {
352 lpUtils.lpSetLoadingEl( elActionExpanded, 1 );
353
354 const dataSend = {
355 action: 'move_trash_lesson',
356 args: {
357 id_url: 'move-trash-lesson',
358 },
359 lesson_id: lessonId,
360 status: 'delete',
361 };
362
363 const callBack = {
364 success: ( response ) => {
365 const { status, message } = response;
366 lpToastify.show( message, status );
367
368 if ( status === 'success' ) {
369 const elLesson = elLessonDelete.closest( '.lesson' );
370 elLesson.style.transition = 'opacity 0.4s ease-out, transform 0.4s ease-out';
371 elLesson.style.opacity = '0';
372 elLesson.style.transform = 'translateX(160px)';
373
374 setTimeout( () => {
375 elLesson.remove();
376 }, 400 );
377 }
378 },
379 error: ( error ) => {
380 lpToastify.show( error.message || error, 'error' );
381 },
382 completed: () => {
383 lpUtils.lpSetLoadingEl( elActionExpanded, 0 );
384 },
385 };
386
387 window.lpAJAXG.fetchAJAX( dataSend, callBack );
388 }
389 } );
390 }
391
392 toggleExpandedAction( args ) {
393 const { target } = args;
394 const elLessonActionExpanded = target.closest(
395 BuilderTabLesson.selectors.elLessonActionExpanded
396 );
397
398 if ( ! elLessonActionExpanded ) {
399 return;
400 }
401
402 const elLessonItem = elLessonActionExpanded.closest( BuilderTabLesson.selectors.elLessonItem );
403
404 if ( ! elLessonItem ) {
405 return;
406 }
407
408 const elExpandedItems = elLessonItem.querySelector(
409 BuilderTabLesson.selectors.elLessonExpandedItems
410 );
411
412 if ( ! elExpandedItems ) {
413 return;
414 }
415
416 this.closeAllExpanded( elExpandedItems );
417 const willOpen = ! elExpandedItems.classList.contains( 'active' );
418
419 if ( willOpen ) {
420 elExpandedItems.classList.add( 'active' );
421 elLessonActionExpanded.classList.add( 'active' );
422 this.setExpandedDirection( elExpandedItems );
423 } else {
424 elExpandedItems.classList.remove( 'active' );
425 elExpandedItems.classList.remove( 'is-dropup' );
426 elLessonActionExpanded.classList.remove( 'active' );
427 }
428 }
429
430 closeAllExpanded( excludeElement = null ) {
431 const allExpandedItems = document.querySelectorAll(
432 `${ BuilderTabLesson.selectors.elLessonExpandedItems }.active`
433 );
434
435 allExpandedItems.forEach( ( item ) => {
436 if ( item === excludeElement ) {
437 return;
438 }
439
440 item.classList.remove( 'active' );
441 item.classList.remove( 'is-dropup' );
442
443 const lessonItem = item.closest( BuilderTabLesson.selectors.elLessonItem );
444
445 if ( ! lessonItem ) {
446 return;
447 }
448
449 const expandedBtn = lessonItem.querySelector(
450 BuilderTabLesson.selectors.elLessonActionExpanded
451 );
452 if ( expandedBtn ) {
453 expandedBtn.classList.remove( 'active' );
454 }
455 } );
456 }
457
458 setExpandedDirection( elExpandedItems ) {
459 if ( ! elExpandedItems ) {
460 return;
461 }
462
463 elExpandedItems.classList.remove( 'is-dropup' );
464
465 const elLesson = elExpandedItems.closest( '.lesson' );
466 if ( ! elLesson ) {
467 return;
468 }
469
470 if ( elLesson.matches( ':last-child' ) ) {
471 elExpandedItems.classList.add( 'is-dropup' );
472 }
473 }
474
475 /* Toggle preview for a lesson in the lesson list */
476 toggleLessonPreview( args ) {
477 const { target } = args;
478 const elPreviewBtn = target.closest( BuilderTabLesson.selectors.elLessonPreview );
479 if ( ! elPreviewBtn ) {
480 return;
481 }
482 if (
483 elPreviewBtn.classList.contains( 'loading' ) ||
484 elPreviewBtn.classList.contains( 'lp-loading' )
485 ) {
486 return;
487 }
488
489 const elLessonItem = elPreviewBtn.closest( BuilderTabLesson.selectors.elLessonItem );
490 if ( ! elLessonItem ) {
491 return;
492 }
493
494 const icon = elPreviewBtn.querySelector( 'a' );
495 if ( ! icon ) {
496 return;
497 }
498
499 const lessonId = elLessonItem.dataset.lessonId || elPreviewBtn.dataset.id || '';
500 if ( ! lessonId ) {
501 return;
502 }
503
504 // Optimistic toggle
505 icon.classList.toggle( 'lp-icon-eye' );
506 icon.classList.toggle( 'lp-icon-eye-slash' );
507
508 const enablePreview = icon.classList.contains( 'lp-icon-eye' );
509
510 lpUtils.lpSetLoadingEl( elPreviewBtn, 1 );
511 elPreviewBtn.classList.add( 'lp-loading' );
512
513 const dataSend = {
514 action: 'builder_update_lesson',
515 args: { id_url: 'builder-update-lesson' },
516 lesson_id: lessonId,
517 lesson_settings: true,
518 _lp_preview: enablePreview ? 'yes' : '',
519 };
520
521 const callBack = {
522 success: ( response ) => {
523 const { status, message } = response;
524 lpToastify.show( message, status );
525
526 if ( status === 'error' ) {
527 // Revert on error
528 icon.classList.toggle( 'lp-icon-eye' );
529 icon.classList.toggle( 'lp-icon-eye-slash' );
530 }
531 },
532 error: ( error ) => {
533 lpToastify.show( error.message || error, 'error' );
534 icon.classList.toggle( 'lp-icon-eye' );
535 icon.classList.toggle( 'lp-icon-eye-slash' );
536 },
537 completed: () => {
538 lpUtils.lpSetLoadingEl( elPreviewBtn, 0 );
539 elPreviewBtn.classList.remove( 'lp-loading' );
540 },
541 };
542
543 window.lpAJAXG.fetchAJAX( dataSend, callBack );
544 }
545
546 replaceItemHtml( elLesson, html ) {
547 if ( ! elLesson || ! html ) {
548 return;
549 }
550 const tmp = document.createElement( 'div' );
551 tmp.innerHTML = html;
552 const newEl = tmp.firstElementChild;
553 if ( newEl ) {
554 elLesson.replaceWith( newEl );
555 }
556 }
557 }
558