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-course / builder-tab-course.js

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

408 lines 10.9 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 SweetAlert from 'sweetalert2';
3 import * as lpToastify from 'lpAssetsJsPath/lpToastify.js';
4 import { SWAL_ICON_DELETE, SWAL_ICON_DUPLICATE, SWAL_ICON_TRASH_DRAFT } from '../swal-icons.js';
5
6 export class BuilderTabCourse {
7 constructor() {
8 this.init();
9 }
10
11 static selectors = {
12 elBtnCreateCourse: 'a.cb-btn-add-new[href]',
13 elCourseItem: '.course-item',
14 elCourseExpandedItems: '.course-action-expanded__items',
15 elCourseDuplicate: '.course-action-expanded__duplicate',
16 elCourseTrash: '.course-action-expanded__trash',
17 elCourseDraft: '.course-action-expanded__draft',
18 elCourseDelete: '.course-action-expanded__delete',
19 elCourseActionExpanded: '.course-action-expanded',
20 elCourseStatus: '.course-status',
21 };
22
23 init() {
24 this.events();
25 }
26
27 events() {
28 if ( BuilderTabCourse._loadedEvents ) {
29 return;
30 }
31 BuilderTabCourse._loadedEvents = true;
32
33 lpUtils.eventHandlers( 'click', [
34 {
35 selector: BuilderTabCourse.selectors.elCourseDuplicate,
36 class: this,
37 callBack: this.duplicateCourse.name,
38 },
39 {
40 selector: BuilderTabCourse.selectors.elCourseTrash,
41 class: this,
42 callBack: this.trashCourse.name,
43 },
44 {
45 selector: BuilderTabCourse.selectors.elCourseDraft,
46 class: this,
47 callBack: this.draftCourse.name,
48 },
49 {
50 selector: BuilderTabCourse.selectors.elCourseDelete,
51 class: this,
52 callBack: this.deleteCourse.name,
53 },
54 {
55 selector: BuilderTabCourse.selectors.elCourseActionExpanded,
56 class: this,
57 callBack: this.toggleExpandedAction.name,
58 },
59 {
60 selector: BuilderTabCourse.selectors.elBtnCreateCourse,
61 callBack: ( args ) => {
62 const { target } = args;
63 const elBtnCreateCourse = target.closest(
64 BuilderTabCourse.selectors.elBtnCreateCourse
65 );
66
67 if ( ! elBtnCreateCourse ) {
68 return;
69 }
70
71 lpUtils.lpSetLoadingEl( elBtnCreateCourse, 1 );
72 },
73 },
74 ] );
75
76 document.addEventListener( 'click', ( e ) => {
77 if ( ! e.target.closest( BuilderTabCourse.selectors.elCourseActionExpanded ) ) {
78 this.closeAllExpanded();
79 }
80 } );
81 }
82
83 duplicateCourse( args ) {
84 const { target } = args;
85 const elCourseDuplicate = target.closest( BuilderTabCourse.selectors.elCourseDuplicate );
86 const elCourseItem = elCourseDuplicate.closest( BuilderTabCourse.selectors.elCourseItem );
87
88 if ( ! elCourseItem ) {
89 return;
90 }
91
92 const courseId = elCourseItem.dataset.courseId || '';
93
94 if ( ! courseId ) {
95 return;
96 }
97
98 SweetAlert.fire( {
99 title: elCourseDuplicate.dataset.title || 'Duplicate Course',
100 text: elCourseDuplicate.dataset.content || 'Are you sure you want to duplicate this course?',
101 iconHtml: SWAL_ICON_DUPLICATE,
102 customClass: { icon: 'lp-cb-swal-icon-html' },
103 showCloseButton: true,
104 showCancelButton: true,
105 cancelButtonText: lpData.i18n.cancel,
106 confirmButtonText: lpData.i18n.yes,
107 reverseButtons: true,
108 } ).then( ( result ) => {
109 if ( result.isConfirmed ) {
110 lpUtils.lpSetLoadingEl( elCourseDuplicate, 1 );
111
112 const dataSend = {
113 action: 'duplicate_course',
114 args: { id_url: 'duplicate-course' },
115 course_id: courseId,
116 };
117
118 const callBack = {
119 success: ( response ) => {
120 const { status, message, data } = response;
121 lpToastify.show( message || 'Duplicated successfully!', status );
122
123 if ( data?.html ) {
124 const elCourse = elCourseDuplicate.closest( '.course' );
125 const elCourseList = elCourse.closest( '.courses-list' ) || elCourse.parentElement;
126
127 if ( elCourseList ) {
128 elCourseList.insertAdjacentHTML( 'afterbegin', data.html );
129 const newCourse = elCourseList.firstElementChild;
130
131 if ( newCourse ) {
132 newCourse.scrollIntoView( { behavior: 'smooth', block: 'nearest' } );
133 newCourse.classList.add( 'highlight-new-course' );
134 setTimeout( () => {
135 newCourse.classList.remove( 'highlight-new-course' );
136 }, 1500 );
137 }
138 }
139 }
140 },
141 error: ( error ) => {
142 lpToastify.show( error.message || error, 'error' );
143 },
144 completed: () => {
145 lpUtils.lpSetLoadingEl( elCourseDuplicate, 0 );
146 },
147 };
148
149 window.lpAJAXG.fetchAJAX( dataSend, callBack );
150 }
151 } );
152 }
153
154 trashCourse( args ) {
155 const { target } = args;
156 const elCourseTrash = target.closest( BuilderTabCourse.selectors.elCourseTrash );
157 const elCourseItem = elCourseTrash.closest( BuilderTabCourse.selectors.elCourseItem );
158
159 if ( ! elCourseItem ) {
160 return;
161 }
162
163 const courseId = elCourseItem.dataset.courseId || '';
164
165 SweetAlert.fire( {
166 title: elCourseTrash.dataset.title || 'Trash Course',
167 text: elCourseTrash.dataset.content || 'Are you sure you want to move this course to trash?',
168 iconHtml: SWAL_ICON_TRASH_DRAFT,
169 customClass: { icon: 'lp-cb-swal-icon-html' },
170 showCloseButton: true,
171 showCancelButton: true,
172 cancelButtonText: lpData.i18n.cancel,
173 confirmButtonText: lpData.i18n.yes,
174 reverseButtons: true,
175 } ).then( ( result ) => {
176 if ( result.isConfirmed ) {
177 lpUtils.lpSetLoadingEl( elCourseTrash, 1 );
178
179 const dataSend = {
180 action: 'move_trash_course',
181 args: { id_url: 'move-trash-course' },
182 course_id: courseId,
183 };
184
185 const callBack = {
186 success: ( response ) => {
187 const { status, message, data } = response;
188 lpToastify.show( message, status );
189
190 if ( data?.status ) {
191 const elCourse = elCourseTrash.closest( '.course' );
192 this.updateStatusUI( elCourse, data.status );
193 }
194 },
195 error: ( error ) => {
196 lpToastify.show( error.message || error, 'error' );
197 },
198 completed: () => {
199 lpUtils.lpSetLoadingEl( elCourseTrash, 0 );
200 },
201 };
202
203 window.lpAJAXG.fetchAJAX( dataSend, callBack );
204 }
205 } );
206 }
207
208 draftCourse( args ) {
209 const { target } = args;
210 const elCourseDraft = target.closest( BuilderTabCourse.selectors.elCourseDraft );
211 const elCourseItem = elCourseDraft.closest( BuilderTabCourse.selectors.elCourseItem );
212
213 if ( ! elCourseItem ) {
214 return;
215 }
216
217 // Check if published to show confirm unpublish modal
218 const statusEl = elCourseItem.querySelector( BuilderTabCourse.selectors.elCourseStatus );
219 const isPublished = statusEl && statusEl.classList.contains( 'publish' );
220 if ( isPublished ) {
221 const confirmMsg =
222 elCourseDraft.dataset.confirmUnpublish ||
223 'Saving as draft will unpublish this item. Are you sure?';
224
225 SweetAlert.fire( {
226 title: 'Draft Course',
227 text: confirmMsg,
228 iconHtml: SWAL_ICON_TRASH_DRAFT,
229 customClass: { icon: 'lp-cb-swal-icon-html' },
230 showCloseButton: true,
231 showCancelButton: true,
232 cancelButtonText: lpData.i18n.cancel,
233 confirmButtonText: lpData.i18n.yes,
234 reverseButtons: true,
235 } ).then( ( result ) => {
236 if ( result.isConfirmed ) {
237 this.draftCourseExecute( elCourseDraft, elCourseItem );
238 }
239 } );
240 return;
241 }
242
243 this.draftCourseExecute( elCourseDraft, elCourseItem );
244 }
245
246 draftCourseExecute( elCourseDraft, elCourseItem ) {
247 lpUtils.lpSetLoadingEl( elCourseDraft, 1 );
248
249 const courseId = elCourseItem.dataset.courseId || '';
250
251 const dataSend = {
252 action: 'move_trash_course',
253 args: { id_url: 'move-trash-course' },
254 course_id: courseId || 0,
255 status: 'draft',
256 };
257
258 const callBack = {
259 success: ( response ) => {
260 const { status, message, data } = response;
261 lpToastify.show( message, status );
262
263 if ( data?.status ) {
264 const elCourse = elCourseDraft.closest( '.course' );
265 this.updateStatusUI( elCourse, data.status );
266 }
267 },
268 error: ( error ) => {
269 lpToastify.show( error.message || error, 'error' );
270 },
271 completed: () => {
272 lpUtils.lpSetLoadingEl( elCourseDraft, 0 );
273 },
274 };
275
276 window.lpAJAXG.fetchAJAX( dataSend, callBack );
277 }
278
279 deleteCourse( args ) {
280 const { target } = args;
281 const elCourseDelete = target.closest( BuilderTabCourse.selectors.elCourseDelete );
282 const elCourseItem = elCourseDelete.closest( BuilderTabCourse.selectors.elCourseItem );
283
284 if ( ! elCourseItem ) {
285 return;
286 }
287
288 const courseId = elCourseItem.dataset.courseId || '';
289
290 if ( ! courseId ) {
291 return;
292 }
293
294 SweetAlert.fire( {
295 title: elCourseDelete.dataset.title || 'Delete Course',
296 text:
297 elCourseDelete.dataset.content ||
298 'Are you sure you want to permanently delete this course?',
299 iconHtml: SWAL_ICON_DELETE,
300 customClass: { icon: 'lp-cb-swal-icon-html' },
301 showCloseButton: true,
302 showCancelButton: true,
303 cancelButtonText: lpData.i18n.cancel,
304 confirmButtonText: lpData.i18n.yes,
305 reverseButtons: true,
306 } ).then( ( result ) => {
307 if ( result.isConfirmed ) {
308 const dataSend = {
309 action: 'move_trash_course',
310 args: { id_url: 'move-trash-course' },
311 course_id: courseId,
312 status: 'delete',
313 };
314
315 const callBack = {
316 success: ( response ) => {
317 const { status, message } = response;
318 lpToastify.show( message, status );
319 const elCourse = elCourseDelete.closest( '.course' );
320
321 elCourse.style.transition = 'opacity 0.4s ease-out, transform 0.4s ease-out';
322 elCourse.style.opacity = '0';
323 elCourse.style.transform = 'translateX(160px)';
324
325 setTimeout( () => {
326 elCourse.remove();
327 }, 400 );
328 },
329 error: ( error ) => {
330 lpToastify.show( error.message || error, 'error' );
331 },
332 };
333
334 window.lpAJAXG.fetchAJAX( dataSend, callBack );
335 }
336 } );
337 }
338
339 toggleExpandedAction( args ) {
340 const { target } = args;
341 const elCourseActionExpanded = target.closest(
342 BuilderTabCourse.selectors.elCourseActionExpanded
343 );
344
345 if ( ! elCourseActionExpanded ) {
346 return;
347 }
348
349 const elCourseItem = elCourseActionExpanded.closest( BuilderTabCourse.selectors.elCourseItem );
350
351 if ( ! elCourseItem ) {
352 return;
353 }
354
355 const elExpandedItems = elCourseItem.querySelector(
356 BuilderTabCourse.selectors.elCourseExpandedItems
357 );
358
359 if ( ! elExpandedItems ) {
360 return;
361 }
362
363 // Close others
364 this.closeAllExpanded( elExpandedItems );
365
366 elExpandedItems.classList.toggle( 'active' );
367 elCourseActionExpanded.classList.toggle( 'active' );
368 }
369
370 closeAllExpanded( excludeElement = null ) {
371 const allExpandedItems = document.querySelectorAll(
372 `${ BuilderTabCourse.selectors.elCourseExpandedItems }.active`
373 );
374
375 allExpandedItems.forEach( ( item ) => {
376 if ( item === excludeElement ) {
377 return;
378 }
379
380 item.classList.remove( 'active' );
381
382 const courseItem = item.closest( BuilderTabCourse.selectors.elCourseItem );
383
384 if ( ! courseItem ) {
385 return;
386 }
387
388 const expandedBtn = courseItem.querySelector(
389 BuilderTabCourse.selectors.elCourseActionExpanded
390 );
391 if ( expandedBtn ) {
392 expandedBtn.classList.remove( 'active' );
393 }
394 } );
395 }
396
397 updateStatusUI( elCourse, status ) {
398 const elStatus = elCourse.querySelector( BuilderTabCourse.selectors.elCourseStatus );
399 const elSpanStatus = elCourse.querySelector(
400 `${ BuilderTabCourse.selectors.elCourseStatus } span`
401 );
402 if ( elSpanStatus && elStatus ) {
403 elStatus.className = 'course-status ' + status;
404 elSpanStatus.textContent = status;
405 }
406 }
407 }
408