PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.3
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.3
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 / edit-course / edit-curriculum / edit-section-item.js

edit-section-item.js in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.3, at assets/src/js/admin/edit-course/edit-curriculum/edit-section-item.js

871 lines 24.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Edit Section item Script on Curriculum
3 *
4 * @version 1.0.3
5 * @since 4.2.8.6
6 */
7 import SweetAlert from 'sweetalert2';
8 import Sortable from 'sortablejs';
9 import * as lpUtils from 'lpAssetsJsPath/utils.js';
10 import * as lpToastify from 'lpAssetsJsPath/lpToastify.js';
11 import { EditCourseCurriculum } from '../edit-curriculum.js';
12 import { EditSection } from './edit-section.js';
13 import { LpPopupSelectItemToAdd } from 'lpAssetsJsPath/lpPopupSelectItemToAdd.js';
14
15 const idUrlHandle = 'edit-course-curriculum';
16
17 const lpPopupSelectItemToAdd = new LpPopupSelectItemToAdd();
18
19 export class EditSectionItem {
20 constructor() {
21 this.courseId = null;
22 this.elCurriculumSections = null;
23 this.sectionIdSelected = null;
24 }
25
26 static selectors = {
27 elSectionListItems: '.section-list-items',
28 elItemClone: '.section-item.clone',
29 elSectionItem: '.section-item',
30 elBtnSelectItemType: '.lp-btn-select-item-type',
31 elAddItemTypeClone: '.lp-add-item-type.clone',
32 elSectionActions: '.section-actions',
33 elAddItemType: '.lp-add-item-type',
34 elAddItemTypeTitleInput: '.lp-add-item-type-title-input',
35 elBtnAddItemCancel: '.lp-btn-add-item-cancel',
36 elBtnAddItem: '.lp-btn-add-item',
37 elItemTitleInput: '.lp-item-title-input',
38 elBtnUpdateItemTitle: '.lp-btn-update-item-title',
39 elBtnCancelUpdateTitle: '.lp-btn-cancel-update-item-title',
40 elBtnDeleteItem: '.lp-btn-delete-item',
41 elBtnSetPreviewItem: '.lp-btn-set-preview-item',
42 };
43
44 init() {
45 this.elEditCurriculum = document.querySelector(
46 `${ EditCourseCurriculum.selectors.idElEditCurriculum }`
47 );
48 this.elCurriculumSections = this.elEditCurriculum.querySelector(
49 `${ EditCourseCurriculum.selectors.elCurriculumSections }`
50 );
51 const elLPTarget = this.elEditCurriculum.closest(
52 `${ EditCourseCurriculum.selectors.LPTarget }`
53 );
54 const dataSend = window.lpAJAXG.getDataSetCurrent( elLPTarget );
55 this.courseId = dataSend.args.course_id;
56
57 this.events();
58 this.sortAbleItem();
59 lpPopupSelectItemToAdd.init();
60 }
61
62 /* Events */
63 events() {
64 // Check and attach events only once
65 if ( EditSectionItem._loadedEvents ) {
66 return;
67 }
68
69 EditSectionItem._loadedEvents = this;
70
71 // Click events
72 lpUtils.eventHandlers( 'click', [
73 {
74 selector: EditSectionItem.selectors.elBtnSelectItemType,
75 class: this,
76 callBack: this.addItemType.name,
77 },
78 {
79 selector: EditSectionItem.selectors.elBtnAddItem,
80 class: this,
81 callBack: this.addItemToSection.name,
82 },
83 {
84 selector: EditSectionItem.selectors.elBtnAddItemCancel,
85 class: this,
86 callBack: this.cancelAddItemType.name,
87 },
88 {
89 selector: EditSectionItem.selectors.elBtnUpdateItemTitle,
90 class: this,
91 callBack: this.updateTitle.name,
92 },
93 {
94 selector: EditSectionItem.selectors.elBtnCancelUpdateTitle,
95 class: this,
96 callBack: this.cancelUpdateTitle.name,
97 },
98 {
99 selector: EditSectionItem.selectors.elBtnDeleteItem,
100 class: this,
101 callBack: this.deleteItem.name,
102 },
103 {
104 selector: LpPopupSelectItemToAdd.selectors.elBtnShowPopupItemsToSelect,
105 class: this,
106 callBack: this.handleShowPopupItemsToSelect.name,
107 },
108 {
109 selector: LpPopupSelectItemToAdd.selectors.elBtnAddItemsSelected,
110 class: lpPopupSelectItemToAdd,
111 callBack: lpPopupSelectItemToAdd.addItemsSelectedToSection.name,
112 callBackHandle: this.addItemsSelectedToSection.bind( this ),
113 },
114 {
115 selector: EditSectionItem.selectors.elBtnSetPreviewItem,
116 class: this,
117 callBack: this.updatePreviewItem.name,
118 },
119 ] );
120
121 // Keyup events
122 lpUtils.eventHandlers( 'keyup', [
123 {
124 selector: EditSectionItem.selectors.elItemTitleInput,
125 class: this,
126 callBack: this.changeTitle.name,
127 },
128 {
129 selector: EditSectionItem.selectors.elAddItemTypeTitleInput,
130 class: this,
131 callBack: this.changeTitleAddNew.name,
132 },
133 ] );
134
135 // Keydown events
136 lpUtils.eventHandlers( 'keydown', [
137 {
138 selector: EditSectionItem.selectors.elAddItemTypeTitleInput,
139 class: this,
140 callBack: this.addItemToSection.name,
141 checkIsEventEnter: true,
142 },
143 {
144 selector: EditSectionItem.selectors.elItemTitleInput,
145 class: this,
146 callBack: this.updateTitle.name,
147 checkIsEventEnter: true,
148 },
149 ] );
150
151 // Focusin events
152 lpUtils.eventHandlers( 'focusin', [
153 {
154 selector: EditSectionItem.selectors.elItemTitleInput,
155 class: this,
156 callBack: this.focusTitleInput.name,
157 },
158 ] );
159
160 // Focusout events
161 lpUtils.eventHandlers( 'focusout', [
162 {
163 selector: EditSectionItem.selectors.elItemTitleInput,
164 class: this,
165 callBack: this.focusTitleInput.name,
166 focusIn: false,
167 },
168 ] );
169 }
170
171 /* Handle show popup items to select - set sectionIdSelected */
172 handleShowPopupItemsToSelect( args ) {
173 const { e, target } = args;
174
175 const elQuizWrap = target.closest( '.lp-edit-quiz-wrap' );
176 if ( elQuizWrap ) {
177 this.sectionIdSelected = null;
178 return;
179 }
180
181 const elSection = target.closest( EditSection.selectors.elSection );
182
183 const elEditCurriculum =
184 target.closest( '#lp-course-edit-curriculum' ) ||
185 target.closest( '.lp-edit-curriculum-wrap' );
186
187 if ( elSection && elEditCurriculum ) {
188 this.sectionIdSelected = elSection.dataset.sectionId;
189 } else {
190 this.sectionIdSelected = null;
191 }
192 }
193
194 /* Add item type */
195 addItemType( args ) {
196 const { e, target } = args;
197
198 const elBtnSelectItemType = target;
199
200 const itemType = elBtnSelectItemType.dataset.itemType;
201 const itemPlaceholder = elBtnSelectItemType.dataset.placeholder;
202 const itemBtnAddText = elBtnSelectItemType.dataset.buttonAddText;
203
204 const elSection = elBtnSelectItemType.closest( `${ EditSection.selectors.elSection }` );
205 const elSectionActions = elSection.querySelector(
206 `${ EditSectionItem.selectors.elSectionActions }`
207 );
208
209 // Insert input item type to add
210 const elAddItemTypeClone = elSectionActions.querySelector(
211 `${ EditSectionItem.selectors.elAddItemTypeClone }`
212 );
213 const elNewItemByType = elAddItemTypeClone.cloneNode( true );
214 const elAddItemTypeInput = elNewItemByType.querySelector(
215 `${ EditSectionItem.selectors.elAddItemTypeTitleInput }`
216 );
217 const elBtnAddItem = elNewItemByType.querySelector(
218 `${ EditSectionItem.selectors.elBtnAddItem }`
219 );
220
221 elNewItemByType.classList.remove( 'clone' );
222 elNewItemByType.classList.add( itemType );
223 lpUtils.lpShowHideEl( elNewItemByType, 1 );
224 elAddItemTypeInput.setAttribute( 'placeholder', itemPlaceholder );
225 elAddItemTypeInput.dataset.itemType = itemType;
226 elBtnAddItem.textContent = itemBtnAddText;
227 elSectionActions.insertAdjacentElement( 'beforebegin', elNewItemByType );
228 elAddItemTypeInput.focus();
229 }
230
231 /* Cancel add item type */
232 cancelAddItemType( args ) {
233 const { e, target } = args;
234 const elAddItemType = target.closest( `${ EditSectionItem.selectors.elAddItemType }` );
235 if ( elAddItemType ) {
236 elAddItemType.remove();
237 }
238 }
239
240 /* Add item to section */
241 addItemToSection( args ) {
242 const { e, target, callBackNest } = args;
243 e.preventDefault();
244
245 const elAddItemType = target.closest( `${ EditSectionItem.selectors.elAddItemType }` );
246 const elSection = elAddItemType.closest( `${ EditSection.selectors.elSection }` );
247 const sectionId = elSection.dataset.sectionId;
248 const elAddItemTypeTitleInput = elAddItemType.querySelector(
249 `${ EditSectionItem.selectors.elAddItemTypeTitleInput }`
250 );
251 const titleValue = elAddItemTypeTitleInput.value.trim();
252 const typeValue = elAddItemTypeTitleInput.dataset.itemType;
253 const message = elAddItemTypeTitleInput.dataset.messEmptyTitle;
254
255 if ( titleValue.length === 0 ) {
256 lpToastify.show( message, 'error' );
257 return;
258 }
259
260 // Clone new section item
261 const elItemClone = elSection.querySelector( `${ EditSectionItem.selectors.elItemClone }` );
262 const elItemNew = elItemClone.cloneNode( true );
263 const elItemTitleInput = elItemNew.querySelector(
264 `${ EditSectionItem.selectors.elItemTitleInput }`
265 );
266
267 elItemNew.classList.remove( 'clone' );
268 elItemNew.classList.add( typeValue );
269 elItemNew.dataset.itemType = typeValue;
270 lpUtils.lpShowHideEl( elItemNew, 1 );
271 lpUtils.lpSetLoadingEl( elItemNew, 1 );
272 elItemTitleInput.value = titleValue;
273 elItemTitleInput.dataset.old = titleValue;
274 elItemClone.insertAdjacentElement( 'beforebegin', elItemNew );
275 elAddItemType.remove();
276
277 // Call ajax to add item to section
278 const callBack = {
279 success: ( response ) => {
280 const { message, status, data } = response;
281
282 lpToastify.show( message, status );
283
284 if ( status === 'error' ) {
285 elItemNew.remove();
286 } else if ( status === 'success' ) {
287 const { section_item, item_link } = data || {};
288 const itemId = section_item.item_id || 0;
289 elItemNew.dataset.itemId = itemId;
290 elItemNew.querySelector( '.edit-link' ).setAttribute( 'href', item_link || '' );
291
292 // Add popup attributes for Course Builder context
293 this.addPopupAttributesToItem( elItemNew, itemId, typeValue );
294
295 // Call callback nest if exists
296 if ( callBackNest && typeof callBackNest.success === 'function' ) {
297 args.elItemNew = elItemNew;
298 callBackNest.success( args );
299 }
300 }
301 },
302 error: ( error ) => {
303 lpToastify.show( error, 'error' );
304 elItemNew.remove();
305 },
306 completed: () => {
307 lpUtils.lpSetLoadingEl( elItemNew, 0 );
308 this.updateCountItems( elSection );
309
310 // Call callback nest if exists
311 if ( callBackNest && typeof callBackNest.completed === 'function' ) {
312 args.elItemNew = elItemNew;
313 callBackNest.completed( args );
314 }
315 },
316 };
317
318 const dataSend = {
319 course_id: this.courseId,
320 action: 'create_item_add_to_section',
321 section_id: sectionId,
322 item_title: titleValue,
323 item_type: typeValue,
324 args: { id_url: idUrlHandle },
325 };
326
327 if ( document.querySelector( '#lp-course-builder' ) ) {
328 dataSend.is_course_builder = 1;
329 }
330
331 window.lpAJAXG.fetchAJAX( dataSend, callBack );
332 }
333
334 /* Typing in title input */
335 changeTitle( args ) {
336 const { target } = args;
337 const elItemTitleInput = target.closest( `${ EditSectionItem.selectors.elItemTitleInput }` );
338 if ( ! elItemTitleInput ) {
339 return;
340 }
341
342 const elSectionItem = elItemTitleInput.closest(
343 `${ EditSectionItem.selectors.elSectionItem }`
344 );
345 if ( ! elSectionItem ) {
346 return;
347 }
348
349 const titleValue = elItemTitleInput.value.trim();
350 const titleValueOld = elItemTitleInput.dataset.old || '';
351
352 if ( titleValue === titleValueOld ) {
353 elSectionItem.classList.remove( 'editing' );
354 } else {
355 elSectionItem.classList.add( 'editing' );
356 }
357 }
358
359 /* Focus in item title input */
360 focusTitleInput( args ) {
361 const { target, focusIn = true } = args;
362
363 const elItemTitleInput = target.closest( `${ EditSectionItem.selectors.elItemTitleInput }` );
364 if ( ! elItemTitleInput ) {
365 return;
366 }
367
368 const elSectionItem = elItemTitleInput.closest(
369 `${ EditSectionItem.selectors.elSectionItem }`
370 );
371 if ( ! elSectionItem ) {
372 return;
373 }
374
375 if ( focusIn ) {
376 elSectionItem.classList.add( 'focus' );
377 } else {
378 elSectionItem.classList.remove( 'focus' );
379 }
380 }
381
382 changeTitleAddNew( args ) {
383 const { target } = args;
384 const elAddItemTypeTitleInput = target.closest(
385 `${ EditSectionItem.selectors.elAddItemTypeTitleInput }`
386 );
387 if ( ! elAddItemTypeTitleInput ) {
388 return;
389 }
390
391 const elAddItemType = elAddItemTypeTitleInput.closest(
392 `${ EditSectionItem.selectors.elAddItemType }`
393 );
394 if ( ! elAddItemType ) {
395 return;
396 }
397
398 const elBtnAddItem = elAddItemType.querySelector(
399 `${ EditSectionItem.selectors.elBtnAddItem }`
400 );
401 if ( ! elBtnAddItem ) {
402 return;
403 }
404
405 const titleValue = elAddItemTypeTitleInput.value.trim();
406 if ( titleValue.length === 0 ) {
407 elBtnAddItem.classList.remove( 'active' );
408 } else {
409 elBtnAddItem.classList.add( 'active' );
410 }
411 }
412
413 /* Update item title */
414 updateTitle( args ) {
415 const { e, target } = args;
416
417 e.preventDefault();
418
419 const elSectionItem = target.closest( `${ EditSectionItem.selectors.elSectionItem }` );
420 if ( ! elSectionItem ) {
421 return;
422 }
423
424 const elSection = elSectionItem.closest( `${ EditSection.selectors.elSection }` );
425 if ( ! elSection ) {
426 return;
427 }
428
429 const elItemTitleInput = elSectionItem.querySelector(
430 `${ EditSectionItem.selectors.elItemTitleInput }`
431 );
432 if ( ! elItemTitleInput ) {
433 return;
434 }
435
436 const itemId = elSectionItem.dataset.itemId;
437 const itemType = elSectionItem.dataset.itemType;
438 const itemTitleValue = elItemTitleInput.value.trim();
439 const titleOld = elItemTitleInput.dataset.old;
440 const message = elItemTitleInput.dataset.messEmptyTitle;
441 if ( itemTitleValue.length === 0 ) {
442 lpToastify.show( message, 'error' );
443 return;
444 }
445
446 if ( itemTitleValue === titleOld ) {
447 return;
448 }
449
450 // Un-focus input item title
451 elItemTitleInput.blur();
452 // show loading
453 lpUtils.lpSetLoadingEl( elSectionItem, 1 );
454 // Call ajax to update item title
455 const callBack = {
456 success: ( response ) => {
457 const { message, status } = response;
458
459 if ( status === 'success' ) {
460 elItemTitleInput.dataset.old = itemTitleValue;
461 } else {
462 elItemTitleInput.value = titleOld;
463 }
464
465 lpToastify.show( message, status );
466 },
467 error: ( error ) => {
468 lpToastify.show( error, 'error' );
469 },
470 completed: () => {
471 lpUtils.lpSetLoadingEl( elSectionItem, 0 );
472 elSectionItem.classList.remove( 'editing' );
473 },
474 };
475
476 const dataSend = {
477 course_id: this.courseId,
478 action: 'update_item_of_section',
479 section_id: elSection.dataset.sectionId,
480 item_id: itemId,
481 item_type: itemType,
482 item_title: itemTitleValue,
483 args: { id_url: idUrlHandle },
484 };
485 window.lpAJAXG.fetchAJAX( dataSend, callBack );
486 }
487
488 /* Cancel update item title */
489 cancelUpdateTitle( args ) {
490 const { e, target } = args;
491 const elBtnCancelUpdateTitle = target.closest(
492 `${ EditSectionItem.selectors.elBtnCancelUpdateTitle }`
493 );
494 if ( ! elBtnCancelUpdateTitle ) {
495 return;
496 }
497
498 const elSectionItem = elBtnCancelUpdateTitle.closest(
499 `${ EditSectionItem.selectors.elSectionItem }`
500 );
501 const elItemTitleInput = elSectionItem.querySelector(
502 `${ EditSectionItem.selectors.elItemTitleInput }`
503 );
504 elItemTitleInput.value = elItemTitleInput.dataset.old || '';
505 elSectionItem.classList.remove( 'editing' );
506 }
507
508 /* Delete item from section */
509 deleteItem( args ) {
510 const { e, target } = args;
511 const elBtnDeleteItem = target.closest( `${ EditSectionItem.selectors.elBtnDeleteItem }` );
512 if ( ! elBtnDeleteItem ) {
513 return;
514 }
515
516 const elSectionItem = elBtnDeleteItem.closest( `${ EditSectionItem.selectors.elSectionItem }` );
517 if ( ! elSectionItem ) {
518 return;
519 }
520
521 const itemId = elSectionItem.dataset.itemId;
522 const elSection = elSectionItem.closest( `${ EditSection.selectors.elSection }` );
523 const sectionId = elSection.dataset.sectionId;
524
525 SweetAlert.fire( {
526 title: elBtnDeleteItem.dataset.title,
527 text: elBtnDeleteItem.dataset.content,
528 icon: 'warning',
529 showCloseButton: true,
530 showCancelButton: true,
531 cancelButtonText: lpData.i18n.cancel,
532 confirmButtonText: lpData.i18n.yes,
533 reverseButtons: true,
534 } ).then( ( result ) => {
535 if ( result.isConfirmed ) {
536 lpUtils.lpSetLoadingEl( elSectionItem, 1 );
537
538 // Call ajax to delete item from section
539 const callBack = {
540 success: ( response ) => {
541 const { message, status } = response;
542
543 lpToastify.show( message, status );
544
545 if ( status === 'success' ) {
546 elSectionItem.remove();
547 }
548 },
549 error: ( error ) => {
550 lpToastify.show( error, 'error' );
551 },
552 completed: () => {
553 lpUtils.lpSetLoadingEl( elSectionItem, 0 );
554 this.updateCountItems( elSection );
555 },
556 };
557
558 const dataSend = {
559 course_id: this.courseId,
560 action: 'delete_item_from_section',
561 section_id: sectionId,
562 item_id: itemId,
563 args: { id_url: idUrlHandle },
564 };
565 window.lpAJAXG.fetchAJAX( dataSend, callBack );
566 }
567 } );
568 }
569
570 /* Sortable items, can drop on multiple sections */
571 sortAbleItem() {
572 const elSectionListItems = this.elCurriculumSections.querySelectorAll(
573 `${ EditSectionItem.selectors.elSectionListItems }`
574 );
575 let itemIdChoose = 0;
576 let elSectionChoose;
577 let sectionIdChoose = 0;
578 let sectionIdEnd = 0;
579 let timeout;
580 elSectionListItems.forEach( ( elItem ) => {
581 new Sortable( elItem, {
582 handle: '.drag',
583 animation: 150,
584 group: { name: 'shared' },
585 onEnd: ( evt ) => {
586 const dataSectionsItems = [];
587
588 const elItemDragged = evt.item;
589 sectionIdEnd = elItemDragged.closest( `${ EditSection.selectors.elSection }` ).dataset
590 .sectionId;
591
592 const dataSend = {
593 course_id: this.courseId,
594 args: { id_url: idUrlHandle },
595 };
596
597 dataSend.action = 'update_item_section_and_position';
598 dataSend.item_id_change = itemIdChoose;
599 dataSend.section_id_new_of_item = sectionIdEnd;
600 dataSend.section_id_old_of_item = sectionIdChoose;
601
602 // Send list items position
603 const section = this.elCurriculumSections.querySelector(
604 `.section[data-section-id="${ sectionIdEnd }"]`
605 );
606 const items = section.querySelectorAll( `${ EditSectionItem.selectors.elSectionItem }` );
607 items.forEach( ( elItem ) => {
608 const itemId = parseInt( elItem.dataset.itemId || 0 );
609 if ( itemId === 0 ) {
610 return;
611 }
612 dataSectionsItems.push( itemId );
613 } );
614 dataSend.items_position = dataSectionsItems;
615
616 // Call ajax to update items position
617 const callBack = {
618 success: ( response ) => {
619 const { message, status } = response;
620
621 lpToastify.show( message, status );
622 },
623 error: ( error ) => {
624 lpToastify.show( error, 'error' );
625 },
626 completed: () => {
627 lpUtils.lpSetLoadingEl( elItemDragged, 0 );
628 this.updateCountItems( section );
629 if ( sectionIdChoose !== sectionIdEnd ) {
630 this.updateCountItems( elSectionChoose );
631 }
632 },
633 };
634
635 lpUtils.lpSetLoadingEl( elItemDragged, 1 );
636 window.lpAJAXG.fetchAJAX( dataSend, callBack );
637 },
638 onMove: ( /*evt*/ ) => {},
639 onChoose: ( evt ) => {
640 const elChooseItem = evt.item;
641 itemIdChoose = elChooseItem.dataset.itemId;
642 elSectionChoose = elChooseItem.closest( `${ EditSection.selectors.elSection }` );
643 sectionIdChoose = elSectionChoose.dataset.sectionId;
644 },
645 onUpdate: ( /*evt*/ ) => {},
646 } );
647 } );
648 }
649
650 /* Add items selected to section */
651 addItemsSelectedToSection( itemsSelectedData ) {
652 // Skip if not in curriculum context (e.g., quiz popup)
653 if ( ! this.sectionIdSelected ) {
654 return;
655 }
656
657 const elSection = document.querySelector(
658 `.section[data-section-id="${ this.sectionIdSelected }"]`
659 );
660
661 // Skip if section element not found
662 if ( ! elSection ) {
663 return;
664 }
665
666 const elItemClone = elSection.querySelector( `${ EditSectionItem.selectors.elItemClone }` );
667
668 // Check if we're in Course Builder context
669 const isCourseBuilder = document.querySelector( '#lp-course-builder' ) !== null;
670
671 itemsSelectedData.forEach( ( item ) => {
672 const elItemNew = elItemClone.cloneNode( true );
673 const elInputTitleNew = elItemNew.querySelector(
674 `${ EditSectionItem.selectors.elItemTitleInput }`
675 );
676
677 elItemNew.dataset.itemId = item.id;
678 elItemNew.classList.add( item.type );
679 elItemNew.classList.remove( 'clone' );
680 elItemNew.dataset.itemType = item.type;
681 elItemNew.querySelector( '.edit-link' ).setAttribute( 'href', item.editLink || '' );
682 elInputTitleNew.value = item.titleSelected || '';
683
684 lpUtils.lpSetLoadingEl( elItemNew, 1 );
685 lpUtils.lpShowHideEl( elItemNew, 1 );
686 elItemClone.insertAdjacentElement( 'beforebegin', elItemNew );
687 } );
688
689 SweetAlert.close();
690
691 const dataSend = {
692 course_id: this.courseId,
693 action: 'add_items_to_section',
694 section_id: this.sectionIdSelected,
695 items: itemsSelectedData,
696 is_course_builder: isCourseBuilder ? 1 : 0,
697 args: { id_url: idUrlHandle },
698 };
699 window.lpAJAXG.fetchAJAX( dataSend, {
700 success: ( response ) => {
701 const { message, status, data } = response;
702 const { html } = data || '';
703 lpToastify.show( message, status );
704
705 itemsSelectedData.forEach( ( item ) => {
706 const elItemAdded = elSection.querySelector(
707 `${ EditSectionItem.selectors.elSectionItem }[data-item-id="${ item.id }"]`
708 );
709 if ( elItemAdded ) {
710 elItemAdded.remove();
711 }
712 } );
713
714 if ( status === 'success' && html ) {
715 // Server returns HTML with popup attributes already included when is_course_builder=1
716 elItemClone.insertAdjacentHTML( 'beforebegin', html );
717 }
718 },
719 error: ( error ) => {
720 lpToastify.show( error, 'error' );
721 },
722 completed: () => {
723 this.updateCountItems( elSection );
724 },
725 } );
726 }
727
728 /* Enable/disable preview item */
729 updatePreviewItem( args ) {
730 const { e, target } = args;
731 const elBtnSetPreviewItem = target.closest(
732 `${ EditSectionItem.selectors.elBtnSetPreviewItem }`
733 );
734 if ( ! elBtnSetPreviewItem ) {
735 return;
736 }
737
738 const elSectionItem = elBtnSetPreviewItem.closest(
739 `${ EditSectionItem.selectors.elSectionItem }`
740 );
741 if ( ! elSectionItem ) {
742 return;
743 }
744
745 const icon = elBtnSetPreviewItem.querySelector( 'a' );
746
747 icon.classList.toggle( 'lp-icon-eye' );
748 icon.classList.toggle( 'lp-icon-eye-slash' );
749
750 const enablePreview = ! icon.classList.contains( 'lp-icon-eye-slash' );
751
752 const itemId = elSectionItem.dataset.itemId;
753 const itemType = elSectionItem.dataset.itemType;
754
755 lpUtils.lpSetLoadingEl( elSectionItem, 1 );
756
757 // Call ajax to update item preview
758 const callBack = {
759 success: ( response ) => {
760 const { message, status } = response;
761
762 lpToastify.show( message, status );
763
764 if ( status === 'error' ) {
765 icon.classList.toggle( 'lp-icon-eye' );
766 icon.classList.toggle( 'lp-icon-eye-slash' );
767 }
768 },
769 error: ( error ) => {
770 lpToastify.show( error, 'error' );
771 icon.classList.toggle( 'lp-icon-eye' );
772 icon.classList.toggle( 'lp-icon-eye-slash' );
773 },
774 completed: () => {
775 lpUtils.lpSetLoadingEl( elSectionItem, 0 );
776 },
777 };
778
779 const dataSend = {
780 course_id: this.courseId,
781 action: 'update_item_preview',
782 item_id: itemId,
783 item_type: itemType,
784 enable_preview: enablePreview ? 1 : 0,
785 args: { id_url: idUrlHandle },
786 };
787 window.lpAJAXG.fetchAJAX( dataSend, callBack );
788 }
789
790 /* Update count items when item add/delete or section delete */
791 updateCountItems( elSection ) {
792 const elEditCurriculum = this.elEditCurriculum;
793 const elCountItemsAll = elEditCurriculum.querySelector( '.total-items' );
794 const elItemsAll = elEditCurriculum.querySelectorAll(
795 `${ EditSectionItem.selectors.elSectionItem }:not(.clone)`
796 );
797 const itemsAllCount = elItemsAll.length;
798
799 elCountItemsAll.dataset.count = itemsAllCount;
800 elCountItemsAll.querySelector( '.count' ).textContent = itemsAllCount;
801
802 // Count items in section
803 const elSectionItemsCount = elSection.querySelector( '.section-items-counts' );
804
805 const elItems = elSection.querySelectorAll(
806 `${ EditSectionItem.selectors.elSectionItem }:not(.clone)`
807 );
808 const itemsCount = elItems.length;
809
810 elSectionItemsCount.dataset.count = itemsCount;
811 elSectionItemsCount.querySelector( '.count' ).textContent = itemsCount;
812 }
813
814 /**
815 * Add popup attributes to item element for Course Builder context.
816 * Only applies when in Course Builder (not admin edit course page).
817 *
818 * @param {HTMLElement} elItem - The item element
819 * @param {number} itemId - The item ID
820 * @param {string} itemType - The item type (lp_lesson, lp_quiz, lp_assignment)
821 */
822 addPopupAttributesToItem( elItem, itemId, itemType ) {
823 // Check if we're in Course Builder context
824 const isCourseBuilder = document.querySelector( '#lp-course-builder' ) !== null;
825
826 if ( ! isCourseBuilder || ! itemId ) {
827 return;
828 }
829
830 const popupType = itemType.replace( /^lp_/, '' );
831 const templateSelector = `#lp-tmpl-builder-popup-curriculum-${ popupType }-course-${ this.courseId }`;
832
833 if ( ! popupType || ! document.querySelector( templateSelector ) ) {
834 return;
835 }
836
837 // Find the edit link element - it's an <a> with class 'edit-link' inside .item-actions
838 const editLink = elItem.querySelector( '.item-actions .edit-link' );
839 if ( ! editLink ) {
840 return;
841 }
842
843 // Get the parent <li> element of the edit link
844 const editBtn = editLink.closest( 'li' );
845 if ( ! editBtn ) {
846 return;
847 }
848
849 // Add popup data attributes based on item type
850 if ( itemType === 'lp_lesson' ) {
851 editBtn.setAttribute( 'data-popup-lesson', itemId );
852 } else if ( itemType === 'lp_quiz' ) {
853 editBtn.setAttribute( 'data-popup-quiz', itemId );
854 }
855
856 // Add popup class to the <li> element
857 editBtn.classList.add( 'lp-btn-edit-item-popup' );
858
859 // Update edit link: remove target="_blank" and update classes for popup behavior
860 editLink.removeAttribute( 'target' );
861 editLink.removeAttribute( 'href' );
862 editLink.classList.add( 'edit-popup-link' );
863
864 // Store additional data for popup on the <li> element
865 editBtn.setAttribute( 'data-course-id', this.courseId );
866 editBtn.setAttribute( 'data-popup-type', popupType );
867 editBtn.setAttribute( 'data-popup-id', itemId );
868 editBtn.setAttribute( 'data-template', templateSelector );
869 }
870 }
871