diff --git a/amd/build/block_myoverview/block_myoverview_view.min.js b/amd/build/block_myoverview/block_myoverview_view.min.js index 2367d99e7..66e14be64 100644 --- a/amd/build/block_myoverview/block_myoverview_view.min.js +++ b/amd/build/block_myoverview/block_myoverview_view.min.js @@ -4,6 +4,6 @@ define("theme_snap/block_myoverview/block_myoverview_view",["exports","jquery"," * * @copyright Copyright (c) 2024 Open LMS (https://www.openlms.net) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue"),yeardata:courseRegion.attr("data-year"),progress:courseRegion.attr("data-progress")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return totalCourseCount>1e3&&((0,_jquery.default)("#yeardropdown").addClass("d-none"),(0,_jquery.default)("#progressdropdown").addClass("d-none")),itemsPerPage.filter((pagingOption=>!(0===pagingOption.value&&totalCourseCount>100)&&pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,yeardata:filters.yeardata,progress:filters.progress}))(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue,yeardata:filters.yeardata,progress:filters.progress}))(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const additionalFilters=getFilterValues(root);let yearFilterData=additionalFilters.yeardata,progressFilterData=additionalFilters.progress;(null!=yearFilterData&&"all"!=yearFilterData||null!=progressFilterData&&"all"!=progressFilterData)&&(itemsPerPage={value:0,active:!0});const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2);const filters=getFilterValues(root);promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{loadedPages.length>0?loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)})):init(root)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue"),yeardata:courseRegion.attr("data-year"),progress:courseRegion.attr("data-progress"),catdata:courseRegion.attr("data-category")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return totalCourseCount>1e3&&((0,_jquery.default)("#yeardropdown").addClass("d-none"),(0,_jquery.default)("#progressdropdown").addClass("d-none"),(0,_jquery.default)("#catdropdown").addClass("d-none")),itemsPerPage.filter((pagingOption=>!(0===pagingOption.value&&totalCourseCount>100)&&pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,yeardata:filters.yeardata,progress:filters.progress,catdata:filters.catdata}))(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue,yeardata:filters.yeardata,progress:filters.progress,catdata:filters.catdata}))(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const additionalFilters=getFilterValues(root);let yearFilterData=additionalFilters.yeardata,catFilterData=additionalFilters.catdata,progressFilterData=additionalFilters.progress;(null!=yearFilterData&&"all"!=yearFilterData||null!=progressFilterData&&"all"!=progressFilterData||null!=catFilterData&&"all"!=catFilterData)&&(itemsPerPage={value:0,active:!0});const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2);const filters=getFilterValues(root);promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{loadedPages.length>0?loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)})):init(root)}})); //# sourceMappingURL=block_myoverview_view.min.js.map \ No newline at end of file diff --git a/amd/build/block_myoverview/block_myoverview_view.min.js.map b/amd/build/block_myoverview/block_myoverview_view.min.js.map index 2e3aacd99..f027ebf1f 100644 --- a/amd/build/block_myoverview/block_myoverview_view.min.js.map +++ b/amd/build/block_myoverview/block_myoverview_view.min.js.map @@ -1 +1 @@ -{"version":3,"file":"block_myoverview_view.min.js","sources":["../../src/block_myoverview/block_myoverview_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Manage the courses view for the overview block.\n *\n * @copyright Copyright (c) 2024 Open LMS (https://www.openlms.net)\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'theme_snap/block_myoverview/block_myoverview_repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n yeardata: courseRegion.attr('data-year'),\n progress: courseRegion.attr('data-progress'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n yeardata: filters.yeardata,\n progress: filters.progress\n });\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n yeardata: filters.yeardata,\n progress: filters.progress\n });\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n if (totalCourseCount > 1000) {\n // Open LMS change: This is the limit to the query in case we use year or completion filters. We are not going to\n // use these filters if we have more than 1000 courses enrolled.\n $('#yeardropdown').addClass('d-none');\n $('#progressdropdown').addClass('d-none');\n }\n return itemsPerPage.filter(pagingOption => {\n if (pagingOption.value === 0 && totalCourseCount > 100) {\n // To minimise performance issues, do not show the \"All\" option if the user is enrolled in more than 100 courses.\n return false;\n }\n return pagingOption.value < totalCourseCount;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n // Open LMS change: If we use the year of progress filters, we are not using pagination because we apply the\n // additional filters to the result of the Core filters. That result is calculated using the pagination, so our\n // filters will be applied only to the first 24 courses in case that the pagination is selected to 24 and so on.\n const additionalFilters = getFilterValues(root);\n let yearFilterData = additionalFilters.yeardata;\n let progressFilterData = additionalFilters.progress;\n if ((yearFilterData != undefined && yearFilterData != \"all\") ||\n (progressFilterData != undefined && progressFilterData != \"all\")) {\n itemsPerPage = {\n value: 0,\n active: true\n };\n }\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Get the current applied filters.\n const filters = getFilterValues(root);\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","yeardata","progress","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","getEnrolledCoursesByTimeline","offset","classification","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","additionalFilters","yearFilterData","progressFilterData","undefined","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"block_myoverview_view.min.js","sources":["../../src/block_myoverview/block_myoverview_view.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Manage the courses view for the overview block.\n *\n * @copyright Copyright (c) 2024 Open LMS (https://www.openlms.net)\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'theme_snap/block_myoverview/block_myoverview_repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n yeardata: courseRegion.attr('data-year'),\n progress: courseRegion.attr('data-progress'),\n catdata: courseRegion.attr('data-category'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n yeardata: filters.yeardata,\n progress: filters.progress,\n catdata: filters.catdata\n });\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n yeardata: filters.yeardata,\n progress: filters.progress,\n catdata: filters.catdata\n });\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n if (totalCourseCount > 1000) {\n // Open LMS change: This is the limit to the query in case we use year or completion filters. We are not going to\n // use these filters if we have more than 1000 courses enrolled.\n $('#yeardropdown').addClass('d-none');\n $('#progressdropdown').addClass('d-none');\n $('#catdropdown').addClass('d-none');\n }\n return itemsPerPage.filter(pagingOption => {\n if (pagingOption.value === 0 && totalCourseCount > 100) {\n // To minimise performance issues, do not show the \"All\" option if the user is enrolled in more than 100 courses.\n return false;\n }\n return pagingOption.value < totalCourseCount;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n // Open LMS change: If we use the year of progress filters, we are not using pagination because we apply the\n // additional filters to the result of the Core filters. That result is calculated using the pagination, so our\n // filters will be applied only to the first 24 courses in case that the pagination is selected to 24 and so on.\n const additionalFilters = getFilterValues(root);\n let yearFilterData = additionalFilters.yeardata;\n let catFilterData = additionalFilters.catdata;\n let progressFilterData = additionalFilters.progress;\n if ((yearFilterData != undefined && yearFilterData != \"all\") ||\n (progressFilterData != undefined && progressFilterData != \"all\") ||\n (catFilterData != undefined && catFilterData != \"all\")) {\n itemsPerPage = {\n value: 0,\n active: true\n };\n }\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Get the current applied filters.\n const filters = getFilterValues(root);\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","yeardata","progress","catdata","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","getEnrolledCoursesByTimeline","offset","classification","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","additionalFilters","yearFilterData","catFilterData","progressFilterData","undefined","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":";;;;;;ipBAoCMA,wBACa,8BADbA,uBAEY,6BAFZA,0BAGe,gCAHfA,oBAIS,yBAGTC,sCAC2B,qBAS3BC,mBAAqB,CAAC,GAAI,GAAI,GAAI,GAAI,OAExCC,YAAc,GAEdC,aAAe,EAEfC,SAAW,EAEXC,UAAY,EAEZC,UAAY,WAQVC,gBAAkBC,aACdC,aAAeD,KAAKE,KAAKC,mBAAUC,WAAWC,cAC7C,CACHC,QAASL,aAAaM,KAAK,gBAC3BC,SAAUP,aAAaM,KAAK,iBAC5BE,KAAMR,aAAaM,KAAK,aACxBG,kBAAmBT,aAAaM,KAAK,0BACrCI,gBAAiBV,aAAaM,KAAK,wBACnCK,iBAAkBX,aAAaM,KAAK,yBACpCM,SAAUZ,aAAaM,KAAK,aAC5BO,SAAUb,aAAaM,KAAK,iBAC5BQ,QAASd,aAAaM,KAAK,mBAM7BS,6BAA+B,CACjCC,2BAA2B,EAC3BC,wBAAwB,EACxBC,mBAAoB,2CAsDlBC,0BAA4B,CAACpB,KAAMqB,WAC9BrB,KAAKE,KAAKC,mBAAUmB,eAAiB,oBAAsBD,SAAW,MAU3EE,yBAA2B,CAACvB,KAAMwB,QAC7BxB,KAAKE,KAAK,iDAAmDsB,MAAQ,MAS1EC,YAAczB,MACTA,KAAKO,KAAK,kBA8CfmB,wBAA0B,CAAC1B,KAAMqB,WAC5BrB,KAAKE,KAAK,iDAAmDmB,SAAW,MAU7EM,2BAA6B,CAAC3B,KAAMqB,WAC/BrB,KAAKE,KAAK,oDAAsDmB,SAAW,MAShFO,gBAAkB,CAAC5B,KAAMqB,kBACrBQ,aAAeF,2BAA2B3B,KAAMqB,UAChDS,UAAYJ,wBAAwB1B,KAAMqB,UAEhDU,wBAAwBV,UAAU,GAAMW,MAAKC,UACrCA,SACAC,OAAOC,QAAQC,aAAaC,WAAYhB,UACxCQ,aAAaS,YAAY,UACzBR,UAAUS,SAAS,UAhDL,EAACvC,KAAMqB,kBACvBmB,cAAgBpB,0BAA0BpB,KAAMqB,UAEhDoB,gBAAkBD,cAActC,KAAKC,mBAAUuC,mBACrDD,gBAAgBH,YAAY,UAC5BK,KAAKC,OAAOH,uBAENI,gBAAkBL,cAActC,KAAKC,mBAAU2C,oBACrDD,gBAAgBN,SAAS,UACzBI,KAAKI,KAAKF,kBAwCFG,CAAkBhD,KAAMqB,WAExB4B,aAAaC,MAAM,yBAA0B,uCAGlDC,MAAMF,aAAaG,YASpBC,qBAAuB,CAACrD,KAAMqB,kBAC1BQ,aAAeF,2BAA2B3B,KAAMqB,UAChDS,UAAYJ,wBAAwB1B,KAAMqB,UAEhDU,wBAAwBV,UAAU,GAAOW,MAAKC,UACtCA,SACAC,OAAOC,QAAQC,aAAakB,YAAajC,UACzCQ,aAAaU,SAAS,UACtBT,UAAUQ,YAAY,UAzFR,EAACtC,KAAMqB,kBACvBmB,cAAgBpB,0BAA0BpB,KAAMqB,UAEhDoB,gBAAkBD,cAActC,KAAKC,mBAAUuC,mBACrDD,gBAAgBF,SAAS,UACzBI,KAAKI,KAAKN,uBAEJI,gBAAkBL,cAActC,KAAKC,mBAAU2C,oBACrDD,gBAAgBP,YAAY,UAC5BK,KAAKC,OAAOC,kBAiFJU,CAAkBvD,KAAMqB,WAExB4B,aAAaC,MAAM,yBAA0B,uCAGlDC,MAAMF,aAAaG,YAUpBI,sBAAwB,CAACxD,KAAMqB,WAC1BrB,KAAKE,KAAK,+CAAiDmB,SAAW,MAU3EoC,sBAAwB,CAACzD,KAAMqB,WAC1BrB,KAAKE,KAAK,+CAAiDmB,SAAW,MAwD3EqC,qBAAuB,CAACrC,SAAUsC,WAGrB,IAAXA,SACAA,OAAS,OAGN,0EAAoDtC,UAAYsC,QAClER,MAAMF,aAAaG,YAStBQ,YAAc,CAAC5D,KAAM6D,YACjBC,UAAY9D,KAAKE,KAAK,8BACtB6D,OAASC,SAASF,UAAUvD,KAAK,gCAInC0D,cADevE,YAAYqE,QACAG,QAAQC,QAAO,CAACC,YAAaC,YACnDR,KAAQQ,QAAQR,IACjBO,YAAYE,KAAKD,SAEdD,cACR,YAGsC,IAA7B1E,YAAYqE,OAAS,GAAqB,OAC5CQ,WAAa7E,YAAYqE,OAAS,GAAGG,QAAQM,MAAM,EAAG,GAG5D9E,YAAY+E,SAAQ,CAACC,WAAYlD,YACzBA,MAAQuC,OAAQ,KACZY,WAAa,QACuB,IAA5BjF,YAAY8B,MAAQ,KAC5BmD,WAAajF,YAAY8B,MAAQ,GAAG0C,QAAQM,MAAM,EAAG,IAEzD9E,YAAY8B,OAAO0C,QAAU,IAAIxE,YAAY8B,OAAO0C,QAAQM,MAAM,MAAOG,gBAIjFV,cAAgB,IAAIA,iBAAkBM,eAItC3E,WAAamE,OAAS,GAAgD,IAA3CrE,YAAYqE,OAAS,GAAGG,QAAQU,OAAc,OACnEC,sBAAwB7E,KAAKE,KAAK,2CACxC4E,oBAAoBC,qBAAoB,mBAAEF,uBAAuBtE,KAAK,MAAOwD,QAGjFrE,YAAYqE,QAAQG,QAAUD,cAG9BtE,qBAGMqF,iBAAmBzD,yBAAyBvB,KAAM+D,QACxDkB,cAAcjF,KAAMN,YAAYqE,SAAS/B,MAAK,CAACkD,KAAMC,KAC1CC,UAAUC,oBAAoBL,iBAAkBE,KAAMC,MAC9DhC,MAAMF,aAAaG,WAGtB1D,YAAY+E,SAAQ,CAACC,WAAYlD,YACzBA,MAAQuC,OAAQ,CACHxC,yBAAyBvB,KAAMwB,OACvC8D,cAYXvD,wBAA0B,CAACV,SAAUsC,SAEhC4B,WAAWC,oBAAoB,CAClCtB,QAAS,CACL,IACU7C,mBACOsC,WAGtB3B,MAAKyD,QAC2B,IAA3BA,OAAOC,SAASd,SAChBlF,YAAY+E,SAAQC,aAChBA,WAAWR,QAAQO,SAAQ,CAACkB,OAAQnE,SAC5BmE,OAAO9B,IAAMxC,WACbqD,WAAWR,QAAQ1C,OAAOoE,YAAcjC,eAI7C,KAIZR,MAAMF,aAAaG,WASpByC,gBAAkB7F,aACd8F,aAAe9F,KAAKE,KAAKC,mBAAUC,WAAWC,QAAQE,KAAK,qBAC3DwF,aAAe/F,KAAKE,KAAKC,mBAAUC,WAAWC,QAAQE,KAAK,4BAC1D6E,UAAUY,OAAOzG,oBAAqB,CACzCuG,aAAcA,aACdC,aAAcA,gBAWhBd,cAAgB,CAACjF,KAAMiG,qBAEnBC,QAAUnG,gBAAgBC,UAE5BmG,gBAAkB,UAElBA,gBADoB,SAApBD,QAAQ5F,QACUf,wBACS,SAApB2G,QAAQ5F,QACGf,uBAEAA,0BAGjB0G,cAI0C,IAAvCG,MAAMC,QAAQJ,YAAY/B,WAC1B+B,YAAY/B,QAAUoC,OAAOC,OAAON,YAAY/B,UAGpD+B,YAAY/B,QAAU+B,YAAY/B,QAAQsC,KAAIb,SAC1CA,OAAOc,mBAAmD,OAA9BP,QAAQxF,kBAC7BiF,UAEPM,YAAY/B,QAAQU,OACbQ,UAAUY,OAAOG,gBAAiB,CACrCjC,QAAS+B,YAAY/B,UAGlB2B,gBAAgB7F,OAhBpB6F,gBAAgB7F,OAuCzB0G,2BAA6B,CAAC1G,KAAMF,mBAChC6G,MAAQ7G,UAAY8G,mBAAmBC,yBAC7C3E,OAAO4E,UAAUH,MAdJ3G,CAAAA,MAEN+G,OAAS/G,KAAKE,KAAKC,mBAAUC,WAAWC,QAAQE,KAAK,cAAewG,OAYnDC,CAAShH,QAU/BiH,iBAAmB,CAACC,YAAalH,YAC/BmH,aAAe1H,mBAAmB+G,KAAIY,YAClCC,QAAS,SACTD,QAAUF,cACVG,QAAS,GAGN,CACHD,MAAOA,MACPC,OAAQA,iBAKVC,iBAAmBtD,SAAShE,KAAKE,KAAKC,mBAAUC,WAAWC,QAAQE,KAAK,yBAA0B,WACpG+G,iBAAmB,0BAGjB,iBAAiB/E,SAAS,8BAC1B,qBAAqBA,SAAS,8BAC9B,gBAAgBA,SAAS,WAExB4E,aAAaI,QAAOC,gBACI,IAAvBA,aAAaJ,OAAeE,iBAAmB,MAI5CE,aAAaJ,MAAQE,oBAa9BG,YAAc,SAACxB,YAAayB,YAAaC,SAAUC,aAASC,oEAAe,KAEzE3D,QAAU+B,YAAY/B,QAAU+B,YAAY/B,QAAU+B,YACtD6B,cAAgB,EAChBC,YAAc,WAGwB,IAA9BrI,YAAYgI,aAA+B,CACnDK,YAAcrI,YAAYgI,aAAaxD,cACjC8D,kBAAoBD,YAAYnD,OAClCoD,kBAAoBL,SAASZ,QAC7Be,cAAgBH,SAASZ,MAAQiB,kBACjCD,YAAc,IAAIrI,YAAYgI,aAAaxD,WAAYA,QAAQM,MAAM,EAAGsD,sBAI5EA,cAAgBH,SAASZ,QAAS,EAClCgB,YAAeJ,SAASZ,MAAQ,EAAK7C,QAAQM,MAAM,EAAGmD,SAASZ,OAAS7C,QAI5ExE,YAAYgI,aAAe,CACvBxD,QAAS6D,mBAIPE,kBAAqC,IAAlBH,cAA0B5D,QAAQM,MAAMsD,cAAe5D,QAAQU,QAAU,GAC9FqD,iBAAiBrD,SACjBlF,YAAYgI,YAAc,GAAK,CAC3BxD,QAAS+D,mBAKbvI,YAAYgI,aAAaxD,QAAQU,OAAS+C,SAASZ,QAAUkB,iBAAiBrD,QAC9EhF,SAAW8H,YACU,OAAjBG,cACAD,QAAQM,eAAeR,mBAEsB,IAAlChI,YAAYgI,YAAc,IACtChI,YAAYgI,YAAc,GAAGxD,QAAQU,OAAS+C,SAASZ,QAC1DnH,SAAW8H,YAAc,GAG7B/H,aAAesG,YAAYkC,YAMzBC,aAAe,KACjBzI,aAAe,EACfD,YAAc,GACdE,SAAW,EACXC,UAAY,GAQVwI,2BAA6B,KAC/BD,eACO,CAAClC,QAASwB,YAAaC,SAAUC,QAAS5H,KAAMsI,SAAUvB,eACvDwB,YAliBO,EAACrC,QAASa,QACpBxB,WAAWiD,6BAA6B,CAC3CC,OAAQ9I,aACRoH,MAAOA,MACP2B,eAAgBxC,QAAQ1F,SACxBC,KAAMyF,QAAQzF,KACdE,gBAAiBuF,QAAQvF,gBACzBC,iBAAkBsF,QAAQtF,iBAC1BC,SAAUqF,QAAQrF,SAClBC,SAAUoF,QAAQpF,SAClBC,QAASmF,QAAQnF,UAwhBG4H,CAChBzC,QACAa,OACF/E,MAAKiE,cACHwB,YAAYxB,YAAayB,YAAaC,SAAUC,SACzC3C,cAAcjF,KAAMN,YAAYgI,iBACxCvE,MAAMF,aAAaG,WAEtBkF,SAAShE,KAAKiE,eAShBK,yBAA2B,KAC7BR,eACO,CAAClC,QAASwB,YAAaC,SAAUC,QAAS5H,KAAMsI,SAAUvB,MAAO8B,oBAC9DC,iBAhiBa,EAAC5C,QAASa,MAAOgC,cACjCxD,WAAWiD,6BAA6B,CAC3CC,OAAQ9I,aACRoH,MAAOA,MACP2B,eAAgB,SAChBjI,KAAMyF,QAAQzF,KACdE,gBAAiBuF,QAAQvF,gBACzBC,iBAAkBsF,QAAQtF,iBAC1BoI,YAAaD,YACblI,SAAUqF,QAAQrF,SAClBC,SAAUoF,QAAQpF,SAClBC,QAASmF,QAAQnF,UAqhBQkI,CACrB/C,QACAa,MACA8B,YACF7G,MAAKiE,cACHwB,YAAYxB,YAAayB,YAAaC,SAAUC,SACzC3C,cAAcjF,KAAMN,YAAYgI,iBACxCvE,MAAMF,aAAaG,WAEtBkF,SAAShE,KAAKwE,oBAWhBI,uBAAyB,SAAClJ,KAAMmJ,qBAAiBN,kEAAa,WAC1D3B,YAAclD,SAAShE,KAAKE,KAAKC,mBAAUC,WAAWC,QAAQE,KAAK,eAAgB,QACrF4G,aAAeF,iBAAiBC,YAAalH,YAE3CoJ,OAAS,IAAWpI,8BAC1BoI,OAAOC,eAAiBvJ,gBAKlBwJ,kBAAoBvJ,gBAAgBC,UACtCuJ,eAAiBD,kBAAkBzI,SACnC2I,cAAgBF,kBAAkBvI,QAClC0I,mBAAqBH,kBAAkBxI,UACpB4I,MAAlBH,gBAAiD,OAAlBA,gBACTG,MAAtBD,oBAAyD,OAAtBA,oBAClBC,MAAjBF,eAA+C,OAAjBA,iBAC/BrC,aAAe,CACXC,MAAO,EACPC,QAAQ,UAIVsC,oBAAsB7E,oBAAoB8E,gBAC5CzC,cACA,CAAC0C,UAAWjC,eACJU,SAAW,UACfuB,UAAUpF,SAAQkD,iBACRD,YAAcC,SAASmC,eACzB/C,MAASY,SAASZ,MAAQ,EAAKY,SAASZ,MAAQ,MAG/ClH,YAAekH,QAChBrH,YAAc,GACdC,aAAe,EACfC,SAAW,GAGXA,WAAa8H,mBAEbE,QAAQM,eAAetI,eACvB0I,SAAShE,KAAKW,cAAcjF,KAAMN,YAAYgI,eAIlD7H,UAAYkH,WAGkC,IAAlCrH,YAAYgI,YAAc,SACQ,IAA9BhI,YAAYgI,eACpBX,OAAS,SAKXb,QAAUnG,gBAAgBC,MAGhCmJ,gBAAgBjD,QAASwB,YAAaC,SAAUC,QAAS5H,KAAMsI,SAAUvB,MAAO8B,eAE7EP,WAEXc,QAGJO,oBAAoB3H,MAAK,CAACkD,KAAMC,MAC5BuB,2BAA2B1G,KAAMF,WAC1BsF,UAAUC,oBAAoBrF,KAAKE,KAAKC,mBAAUC,WAAWC,QAAS6E,KAAMC,OACpFhC,MAAMF,aAAaG,YASpB2G,uBAAyB,CAAC/J,KAAMgK,QAElCC,aAAaC,OAAOlK,KAAM,CACtBiK,aAAaE,OAAOC,WAGxBpK,KAAKqK,GAAGJ,aAAaE,OAAOC,SAAUjK,mBAAUmK,sBAAsB,CAACC,EAAGC,cAChEC,WAAY,mBAAEF,EAAEG,QAAQC,QAAQxK,mBAAUmK,sBAC1CjJ,SAAWI,YAAYgJ,WAC7B7I,gBAAgB5B,KAAMqB,UACtBmJ,KAAKI,cAAcC,oBAGvB7K,KAAKqK,GAAGJ,aAAaE,OAAOC,SAAUjK,mBAAU2K,yBAAyB,CAACP,EAAGC,cACnEC,WAAY,mBAAEF,EAAEG,QAAQC,QAAQxK,mBAAU2K,yBAC1CzJ,SAAWI,YAAYgJ,WAC7BpH,qBAAqBrD,KAAMqB,UAC3BmJ,KAAKI,cAAcC,oBAGvB7K,KAAKqK,GAAGJ,aAAaE,OAAOC,SAAUjK,mBAAUmB,gBAAgB,CAACiJ,EAAGC,QAChEA,KAAKI,cAAcC,oBAGvB7K,KAAKqK,GAAGJ,aAAaE,OAAOC,SAAUjK,mBAAU4K,oBAAoB,CAACR,EAAGC,cAC9DE,QAAS,mBAAEH,EAAEG,QAAQC,QAAQxK,mBAAU4K,oBACvC1J,SAAWI,YAAYiJ,QAxelB,EAAC1K,KAAMqB,kBAChB2J,WAAaxH,sBAAsBxD,KAAMqB,UACzC4J,WAAaxH,sBAAsBzD,KAAMqB,UACzC6E,QAAUnG,gBAAgBC,MAEhC0D,qBAAqBrC,UAAU,GAI3B6E,QAAQ1F,WAAahB,uCACrBoE,YAAY5D,KAAMqB,UAGtB2J,WAAWzI,SAAS,UACpB0I,WAAW3I,YAAY,WA2dnB4I,CAAWlL,KAAMqB,UACjBmJ,KAAKI,cAAcC,oBAGvB7K,KAAKqK,GAAGJ,aAAaE,OAAOC,SAAUjK,mBAAUgL,oBAAoB,CAACZ,EAAGC,cAC9DE,QAAS,mBAAEH,EAAEG,QAAQC,QAAQxK,mBAAUgL,oBACvC9J,SAAWI,YAAYiJ,QAxdlB,EAAC1K,KAAMqB,kBAChB2J,WAAaxH,sBAAsBxD,KAAMqB,UACzC4J,WAAaxH,sBAAsBzD,KAAMqB,UACzC6E,QAAUnG,gBAAgBC,MAEhC0D,qBAAqBrC,SAAU,MAI3B6E,QAAQ1F,WAAahB,uCACrBoE,YAAY5D,KAAMqB,UAGtB2J,WAAW1I,YAAY,UACvB2I,WAAW1I,SAAS,WA2chB6I,CAAWpL,KAAMqB,UACjBmJ,KAAKI,cAAcC,0BAIjBQ,MAAQrB,KAAKsB,cAAcnL,mBAAUE,OAAOkL,aAC5CC,UAAYxB,KAAKsB,cAAcnL,mBAAUE,OAAOmL,WAEtDA,UAAUC,iBAAiB,SAAS,KAChCJ,MAAMjE,MAAQ,GACdiE,MAAMK,QACNC,YAAYH,UAAWxL,SAG3BqL,MAAMI,iBAAiB,SAAS,oBAAS,KACjB,KAAhBJ,MAAMjE,MACNuE,YAAYH,UAAWxL,OAEvB6H,aAAa2D,WACbtC,uBAAuBlJ,KAAM4I,2BAA4ByC,MAAMjE,MAAMwE,WAE1E,OASMD,YAAc,CAACH,UAAWxL,QACnCwL,UAAUK,UAAUC,IAAI,UACxBC,KAAK/L,8CAQH6H,aAAgB2D,YAClBA,UAAUK,UAAUvG,OAAO,WAQlByG,KAAO/L,UAChBA,MAAO,mBAAEA,MACTN,YAAc,GACdE,SAAW,EACXD,aAAe,GAEVK,KAAKO,KAAK,aAAc,OACnByJ,KAAOgC,SAASV,cAAcnL,mBAAUE,OAAO4L,aACrDlC,uBAAuB/J,KAAMgK,MAC7BlK,UAAY,oBAAsBE,KAAKO,KAAK,MAAQ,IAAM2L,KAAKC,SAC/DnM,KAAKO,KAAK,aAAa,GAG3B2I,uBAAuBlJ,KAAMqI,iEAYZrI,OACbN,YAAYkF,OAAS,EACrBlF,YAAY+E,SAAQ,CAACC,WAAYlD,aACzBwD,iBAAmBzD,yBAAyBvB,KAAMwB,OACtDyD,cAAcjF,KAAM0E,YAAY1C,MAAK,CAACkD,KAAMC,KACjCC,UAAUC,oBAAoBL,iBAAkBE,KAAMC,MAC9DhC,MAAMF,aAAaG,cAG1B2I,KAAK/L"} \ No newline at end of file diff --git a/amd/build/block_myoverview/block_myoverview_view_nav.min.js b/amd/build/block_myoverview/block_myoverview_view_nav.min.js index 4b57f2968..be7f4aa4a 100644 --- a/amd/build/block_myoverview/block_myoverview_view_nav.min.js +++ b/amd/build/block_myoverview/block_myoverview_view_nav.min.js @@ -4,6 +4,6 @@ define("theme_snap/block_myoverview/block_myoverview_view_nav",["exports","jquer * * @copyright Copyright (c) 2024 Open LMS (https://www.openlms.net) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),CustomEvents=_interopRequireWildcard(CustomEvents),_notification=_interopRequireDefault(_notification),View=_interopRequireWildcard(View),_selectors=_interopRequireDefault(_selectors);const updatePreferences=(filter,value)=>{let type=null;return type="display"===filter?"block_myoverview_user_view_preference":"sort"===filter?"block_myoverview_user_sort_preference":"customfieldvalue"===filter?"block_myoverview_user_grouping_customfieldvalue_preference":"year"===filter?"snap_my_courses_year_user_preference":"progress"===filter?"snap_my_courses_progress_user_preference":"block_myoverview_user_grouping_preference",(0,_repository.setUserPreference)(type,value).catch(_notification.default.exception)};_exports.init=root=>{(root=>{const Selector=root.find(_selectors.default.FILTERS);CustomEvents.define(Selector,[CustomEvents.events.activate]),Selector.on(CustomEvents.events.activate,_selectors.default.FILTER_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-filter"),pref=option.attr("data-pref"),customfieldvalue=option.attr("data-customfieldvalue");root.find(_selectors.default.courseView.region).attr("data-"+filter,option.attr("data-value")),updatePreferences(filter,pref),customfieldvalue&&(root.find(_selectors.default.courseView.region).attr("data-customfieldvalue",customfieldvalue),updatePreferences("customfieldvalue",customfieldvalue));const page=document.querySelector(_selectors.default.region.selectBlock),input=page.querySelector(_selectors.default.region.searchInput);if(""!==input.value){const clearIcon=page.querySelector(_selectors.default.region.clearIcon);input.value="",View.clearSearch(clearIcon,root)}else View.init(root);data.originalEvent.preventDefault()})),Selector.on(CustomEvents.events.activate,_selectors.default.DISPLAY_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-display-option"),pref=option.attr("data-pref");root.find(_selectors.default.courseView.region).attr("data-display",option.attr("data-value")),updatePreferences(filter,pref),View.reset(root),data.originalEvent.preventDefault()}))})(root=(0,_jquery.default)(root))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),CustomEvents=_interopRequireWildcard(CustomEvents),_notification=_interopRequireDefault(_notification),View=_interopRequireWildcard(View),_selectors=_interopRequireDefault(_selectors);const updatePreferences=(filter,value)=>{let type=null;return type="display"===filter?"block_myoverview_user_view_preference":"sort"===filter?"block_myoverview_user_sort_preference":"customfieldvalue"===filter?"block_myoverview_user_grouping_customfieldvalue_preference":"year"===filter?"snap_my_courses_year_user_preference":"progress"===filter?"snap_my_courses_progress_user_preference":"category"===filter?"snap_my_courses_category_user_preference":"block_myoverview_user_grouping_preference",(0,_repository.setUserPreference)(type,value).catch(_notification.default.exception)};_exports.init=root=>{(root=>{const Selector=root.find(_selectors.default.FILTERS);CustomEvents.define(Selector,[CustomEvents.events.activate]),Selector.on(CustomEvents.events.activate,_selectors.default.FILTER_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-filter"),pref=option.attr("data-pref"),customfieldvalue=option.attr("data-customfieldvalue");root.find(_selectors.default.courseView.region).attr("data-"+filter,option.attr("data-value")),updatePreferences(filter,pref),customfieldvalue&&(root.find(_selectors.default.courseView.region).attr("data-customfieldvalue",customfieldvalue),updatePreferences("customfieldvalue",customfieldvalue));const page=document.querySelector(_selectors.default.region.selectBlock),input=page.querySelector(_selectors.default.region.searchInput);if(""!==input.value){const clearIcon=page.querySelector(_selectors.default.region.clearIcon);input.value="",View.clearSearch(clearIcon,root)}else View.init(root);data.originalEvent.preventDefault()})),Selector.on(CustomEvents.events.activate,_selectors.default.DISPLAY_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-display-option"),pref=option.attr("data-pref");root.find(_selectors.default.courseView.region).attr("data-display",option.attr("data-value")),updatePreferences(filter,pref),View.reset(root),data.originalEvent.preventDefault()}))})(root=(0,_jquery.default)(root))}})); //# sourceMappingURL=block_myoverview_view_nav.min.js.map \ No newline at end of file diff --git a/amd/build/block_myoverview/block_myoverview_view_nav.min.js.map b/amd/build/block_myoverview/block_myoverview_view_nav.min.js.map index ad822df63..0e0753e5a 100644 --- a/amd/build/block_myoverview/block_myoverview_view_nav.min.js.map +++ b/amd/build/block_myoverview/block_myoverview_view_nav.min.js.map @@ -1 +1 @@ -{"version":3,"file":"block_myoverview_view_nav.min.js","sources":["../../src/block_myoverview/block_myoverview_view_nav.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Manage the timeline view navigation for the overview block.\n *\n * @copyright Copyright (c) 2024 Open LMS (https://www.openlms.net)\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\nimport * as View from 'theme_snap/block_myoverview/block_myoverview_view';\nimport SELECTORS from 'block_myoverview/selectors';\n\n/**\n * Update the user preference for the block.\n *\n * @param {String} filter The type of filter: display/sort/grouping.\n * @param {String} value The current preferred value.\n * @return {Promise}\n */\nconst updatePreferences = (filter, value) => {\n let type = null;\n if (filter === 'display') {\n type = 'block_myoverview_user_view_preference';\n } else if (filter === 'sort') {\n type = 'block_myoverview_user_sort_preference';\n } else if (filter === 'customfieldvalue') {\n type = 'block_myoverview_user_grouping_customfieldvalue_preference';\n } else if (filter === 'year') {\n type = 'snap_my_courses_year_user_preference';\n } else if (filter === 'progress') {\n type = 'snap_my_courses_progress_user_preference';\n } else {\n type = 'block_myoverview_user_grouping_preference';\n }\n\n return setUserPreference(type, value)\n .catch(Notification.exception);\n};\n\n/**\n * Event listener for the Display filter (cards, list).\n *\n * @param {object} root The root element for the overview block\n */\nconst registerSelector = root => {\n\n const Selector = root.find(SELECTORS.FILTERS);\n\n CustomEvents.define(Selector, [CustomEvents.events.activate]);\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.FILTER_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n const filter = option.attr('data-filter');\n const pref = option.attr('data-pref');\n const customfieldvalue = option.attr('data-customfieldvalue');\n\n root.find(SELECTORS.courseView.region).attr('data-' + filter, option.attr('data-value'));\n updatePreferences(filter, pref);\n\n if (customfieldvalue) {\n root.find(SELECTORS.courseView.region).attr('data-customfieldvalue', customfieldvalue);\n updatePreferences('customfieldvalue', customfieldvalue);\n }\n\n // Reset the views.\n\n // Check if the user is currently in a searching state, if so we'll reset it.\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n input.value = '';\n // Triggers the init so wont need to call it again.\n View.clearSearch(clearIcon, root);\n } else {\n View.init(root);\n }\n\n data.originalEvent.preventDefault();\n }\n );\n\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.DISPLAY_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n return;\n }\n\n const filter = option.attr('data-display-option');\n const pref = option.attr('data-pref');\n\n root.find(SELECTORS.courseView.region).attr('data-display', option.attr('data-value'));\n updatePreferences(filter, pref);\n View.reset(root);\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the myoverview block\n */\nexport const init = root => {\n root = $(root);\n registerSelector(root);\n};\n"],"names":["updatePreferences","filter","value","type","catch","Notification","exception","root","Selector","find","SELECTORS","FILTERS","CustomEvents","define","events","activate","on","FILTER_OPTION","e","data","option","target","hasClass","attr","pref","customfieldvalue","courseView","region","page","document","querySelector","selectBlock","input","searchInput","clearIcon","View","clearSearch","init","originalEvent","preventDefault","DISPLAY_OPTION","reset","registerSelector"],"mappings":";;;;;;wTAoCMA,kBAAoB,CAACC,OAAQC,aAC3BC,KAAO,YAEPA,KADW,YAAXF,OACO,wCACW,SAAXA,OACA,wCACW,qBAAXA,OACA,6DACW,SAAXA,OACA,uCACW,aAAXA,OACA,2CAEA,6CAGJ,iCAAkBE,KAAMD,OAC1BE,MAAMC,sBAAaC,0BAiFRC,OAzEKA,CAAAA,aAEfC,SAAWD,KAAKE,KAAKC,mBAAUC,SAErCC,aAAaC,OAAOL,SAAU,CAACI,aAAaE,OAAOC,WACnDP,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAUO,eACV,CAACC,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAKdrB,OAASmB,OAAOG,KAAK,eACrBC,KAAOJ,OAAOG,KAAK,aACnBE,iBAAmBL,OAAOG,KAAK,yBAErChB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,QAAUtB,OAAQmB,OAAOG,KAAK,eAC1EvB,kBAAkBC,OAAQuB,MAEtBC,mBACAlB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,wBAAyBE,kBACrEzB,kBAAkB,mBAAoByB,yBAMpCG,KAAOC,SAASC,cAAcpB,mBAAUiB,OAAOI,aAC/CC,MAAQJ,KAAKE,cAAcpB,mBAAUiB,OAAOM,gBAC9B,KAAhBD,MAAM9B,MAAc,OACdgC,UAAYN,KAAKE,cAAcpB,mBAAUiB,OAAOO,WACtDF,MAAM9B,MAAQ,GAEdiC,KAAKC,YAAYF,UAAW3B,WAE5B4B,KAAKE,KAAK9B,MAGdY,KAAKmB,cAAcC,oBAI3B/B,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAU8B,gBACV,CAACtB,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAIdrB,OAASmB,OAAOG,KAAK,uBACrBC,KAAOJ,OAAOG,KAAK,aAEzBhB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,eAAgBH,OAAOG,KAAK,eACxEvB,kBAAkBC,OAAQuB,MAC1BW,KAAKM,MAAMlC,MACXY,KAAKmB,cAAcC,qBAa3BG,CADAnC,MAAO,mBAAEA"} \ No newline at end of file +{"version":3,"file":"block_myoverview_view_nav.min.js","sources":["../../src/block_myoverview/block_myoverview_view_nav.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Manage the timeline view navigation for the overview block.\n *\n * @copyright Copyright (c) 2024 Open LMS (https://www.openlms.net)\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\nimport * as View from 'theme_snap/block_myoverview/block_myoverview_view';\nimport SELECTORS from 'block_myoverview/selectors';\n\n/**\n * Update the user preference for the block.\n *\n * @param {String} filter The type of filter: display/sort/grouping.\n * @param {String} value The current preferred value.\n * @return {Promise}\n */\nconst updatePreferences = (filter, value) => {\n let type = null;\n if (filter === 'display') {\n type = 'block_myoverview_user_view_preference';\n } else if (filter === 'sort') {\n type = 'block_myoverview_user_sort_preference';\n } else if (filter === 'customfieldvalue') {\n type = 'block_myoverview_user_grouping_customfieldvalue_preference';\n } else if (filter === 'year') {\n type = 'snap_my_courses_year_user_preference';\n } else if (filter === 'progress') {\n type = 'snap_my_courses_progress_user_preference';\n } else if (filter === 'category') {\n type = 'snap_my_courses_category_user_preference';\n } else {\n type = 'block_myoverview_user_grouping_preference';\n }\n\n return setUserPreference(type, value)\n .catch(Notification.exception);\n};\n\n/**\n * Event listener for the Display filter (cards, list).\n *\n * @param {object} root The root element for the overview block\n */\nconst registerSelector = root => {\n\n const Selector = root.find(SELECTORS.FILTERS);\n\n CustomEvents.define(Selector, [CustomEvents.events.activate]);\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.FILTER_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n const filter = option.attr('data-filter');\n const pref = option.attr('data-pref');\n const customfieldvalue = option.attr('data-customfieldvalue');\n\n root.find(SELECTORS.courseView.region).attr('data-' + filter, option.attr('data-value'));\n updatePreferences(filter, pref);\n\n if (customfieldvalue) {\n root.find(SELECTORS.courseView.region).attr('data-customfieldvalue', customfieldvalue);\n updatePreferences('customfieldvalue', customfieldvalue);\n }\n\n // Reset the views.\n\n // Check if the user is currently in a searching state, if so we'll reset it.\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n input.value = '';\n // Triggers the init so wont need to call it again.\n View.clearSearch(clearIcon, root);\n } else {\n View.init(root);\n }\n\n data.originalEvent.preventDefault();\n }\n );\n\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.DISPLAY_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n return;\n }\n\n const filter = option.attr('data-display-option');\n const pref = option.attr('data-pref');\n\n root.find(SELECTORS.courseView.region).attr('data-display', option.attr('data-value'));\n updatePreferences(filter, pref);\n View.reset(root);\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the myoverview block\n */\nexport const init = root => {\n root = $(root);\n registerSelector(root);\n};\n"],"names":["updatePreferences","filter","value","type","catch","Notification","exception","root","Selector","find","SELECTORS","FILTERS","CustomEvents","define","events","activate","on","FILTER_OPTION","e","data","option","target","hasClass","attr","pref","customfieldvalue","courseView","region","page","document","querySelector","selectBlock","input","searchInput","clearIcon","View","clearSearch","init","originalEvent","preventDefault","DISPLAY_OPTION","reset","registerSelector"],"mappings":";;;;;;wTAoCMA,kBAAoB,CAACC,OAAQC,aAC3BC,KAAO,YAEPA,KADW,YAAXF,OACO,wCACW,SAAXA,OACA,wCACW,qBAAXA,OACA,6DACW,SAAXA,OACA,uCACW,aAAXA,OACA,2CACW,aAAXA,OACA,2CAEA,6CAGJ,iCAAkBE,KAAMD,OAC1BE,MAAMC,sBAAaC,0BAiFRC,OAzEKA,CAAAA,aAEfC,SAAWD,KAAKE,KAAKC,mBAAUC,SAErCC,aAAaC,OAAOL,SAAU,CAACI,aAAaE,OAAOC,WACnDP,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAUO,eACV,CAACC,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAKdrB,OAASmB,OAAOG,KAAK,eACrBC,KAAOJ,OAAOG,KAAK,aACnBE,iBAAmBL,OAAOG,KAAK,yBAErChB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,QAAUtB,OAAQmB,OAAOG,KAAK,eAC1EvB,kBAAkBC,OAAQuB,MAEtBC,mBACAlB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,wBAAyBE,kBACrEzB,kBAAkB,mBAAoByB,yBAMpCG,KAAOC,SAASC,cAAcpB,mBAAUiB,OAAOI,aAC/CC,MAAQJ,KAAKE,cAAcpB,mBAAUiB,OAAOM,gBAC9B,KAAhBD,MAAM9B,MAAc,OACdgC,UAAYN,KAAKE,cAAcpB,mBAAUiB,OAAOO,WACtDF,MAAM9B,MAAQ,GAEdiC,KAAKC,YAAYF,UAAW3B,WAE5B4B,KAAKE,KAAK9B,MAGdY,KAAKmB,cAAcC,oBAI3B/B,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAU8B,gBACV,CAACtB,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAIdrB,OAASmB,OAAOG,KAAK,uBACrBC,KAAOJ,OAAOG,KAAK,aAEzBhB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,eAAgBH,OAAOG,KAAK,eACxEvB,kBAAkBC,OAAQuB,MAC1BW,KAAKM,MAAMlC,MACXY,KAAKmB,cAAcC,qBAa3BG,CADAnC,MAAO,mBAAEA"} \ No newline at end of file diff --git a/amd/src/block_myoverview/block_myoverview_view.js b/amd/src/block_myoverview/block_myoverview_view.js index b70d84488..f34fa2e6f 100644 --- a/amd/src/block_myoverview/block_myoverview_view.js +++ b/amd/src/block_myoverview/block_myoverview_view.js @@ -80,6 +80,7 @@ const getFilterValues = root => { customfieldvalue: courseRegion.attr('data-customfieldvalue'), yeardata: courseRegion.attr('data-year'), progress: courseRegion.attr('data-progress'), + catdata: courseRegion.attr('data-category'), }; }; @@ -107,7 +108,8 @@ const getMyCourses = (filters, limit) => { customfieldname: filters.customfieldname, customfieldvalue: filters.customfieldvalue, yeardata: filters.yeardata, - progress: filters.progress + progress: filters.progress, + catdata: filters.catdata }); }; @@ -129,7 +131,8 @@ const getSearchMyCourses = (filters, limit, searchValue) => { customfieldvalue: filters.customfieldvalue, searchvalue: searchValue, yeardata: filters.yeardata, - progress: filters.progress + progress: filters.progress, + catdata: filters.catdata }); }; @@ -557,6 +560,7 @@ const itemsPerPageFunc = (pagingLimit, root) => { // use these filters if we have more than 1000 courses enrolled. $('#yeardropdown').addClass('d-none'); $('#progressdropdown').addClass('d-none'); + $('#catdropdown').addClass('d-none'); } return itemsPerPage.filter(pagingOption => { if (pagingOption.value === 0 && totalCourseCount > 100) { @@ -693,9 +697,11 @@ const initializePagedContent = (root, promiseFunction, inputValue = null) => { // filters will be applied only to the first 24 courses in case that the pagination is selected to 24 and so on. const additionalFilters = getFilterValues(root); let yearFilterData = additionalFilters.yeardata; + let catFilterData = additionalFilters.catdata; let progressFilterData = additionalFilters.progress; if ((yearFilterData != undefined && yearFilterData != "all") || - (progressFilterData != undefined && progressFilterData != "all")) { + (progressFilterData != undefined && progressFilterData != "all") || + (catFilterData != undefined && catFilterData != "all")) { itemsPerPage = { value: 0, active: true diff --git a/amd/src/block_myoverview/block_myoverview_view_nav.js b/amd/src/block_myoverview/block_myoverview_view_nav.js index c830b02ff..7a522def1 100644 --- a/amd/src/block_myoverview/block_myoverview_view_nav.js +++ b/amd/src/block_myoverview/block_myoverview_view_nav.js @@ -46,7 +46,9 @@ const updatePreferences = (filter, value) => { type = 'snap_my_courses_year_user_preference'; } else if (filter === 'progress') { type = 'snap_my_courses_progress_user_preference'; - } else { + } else if (filter === 'category') { + type = 'snap_my_courses_category_user_preference'; + } else { type = 'block_myoverview_user_grouping_preference'; } diff --git a/classes/output/block_myoverview_renderer.php b/classes/output/block_myoverview_renderer.php index 9db0eb955..044f13350 100644 --- a/classes/output/block_myoverview_renderer.php +++ b/classes/output/block_myoverview_renderer.php @@ -39,7 +39,7 @@ class block_myoverview_renderer extends \block_myoverview\output\renderer { * @return string HTML string */ public function render_main(main $main) { - global $USER; + global $USER, $DB; if (!count(enrol_get_all_users_courses($USER->id, true))) { return $this->render_from_template( @@ -52,6 +52,7 @@ public function render_main(main $main) { $yearpreference = get_user_preferences('snap_my_courses_year_user_preference') ?? 'all'; $progresspreference = get_user_preferences('snap_my_courses_progress_user_preference') ?? 'all'; + $categorypreference = get_user_preferences('snap_my_courses_category_user_preference') ?? 'all'; $data['progresspreference'] = $progresspreference; if ($yearpreference != 'all') { @@ -61,6 +62,17 @@ public function render_main(main $main) { } $data['yearplaceholder'] = $yearplaceholder; + if ($categorypreference != 'all') { + $categoryselect = $DB->get_record('course_categories', ['id' => $categorypreference]); + $categoryplaceholder = $categoryselect->name; + $categorypreferenceplaceholder = $categoryselect->name; + + } else { + $categorypreferenceplaceholder = 'all'; + $categoryplaceholder = 'Category'; + } + $data['categoryplaceholder'] = $categoryplaceholder; + if ($progresspreference == 'completed') { $data['completed'] = true; $data['completionplaceholder'] = get_string('completed', 'moodle'); @@ -73,7 +85,10 @@ public function render_main(main $main) { $courses = enrol_get_my_courses('enddate', 'fullname ASC, id DESC'); $coursesyears = []; + $coursescats = []; foreach ($courses as $course) { + $category = \core_course_category::get($course->category, IGNORE_MISSING); + $categoryname = $category->name; if (!empty($course->enddate)) { $endyear = userdate($course->enddate, '%Y'); if ($yearpreference == $endyear) { @@ -98,7 +113,30 @@ public function render_main(main $main) { $yearitem->$endyear = html_writer::tag('li', $yearlink); $coursesyears[$endyear] = $yearitem; } + $categoryid = $category->id; + if ($categorypreference == $categoryid) { + $catlink = html_writer::tag('a', $categoryname, [ + 'class' => 'dropdown-item', + 'href' => '#', + 'data-filter' => 'category', + 'data-pref' => $categoryid, + 'data-value' => $categoryid, + 'aria-current' => 'true', + ]); + } else { + $catlink = html_writer::tag('a', $categoryname, [ + 'class' => 'dropdown-item', + 'href' => '#', + 'data-filter' => 'category', + 'data-pref' => $categoryid, + 'data-value' => $categoryid, + ]); + } + $catitem = new stdClass(); + $catitem->$categoryid = html_writer::tag('li', $catlink); + $coursescats[$categoryid] = $catitem; ksort($coursesyears); + ksort($coursescats); } if (!empty($coursesyears)) { $allyearslink = html_writer::tag('a', get_string('allyears', 'theme_snap'),[ @@ -115,6 +153,21 @@ public function render_main(main $main) { $data['years'] = $yearslist; $data['yearpreference'] = $yearpreference; } + if (!empty($coursescats)) { + $allcatslink = html_writer::tag('a', get_string('allcat', 'theme_snap'),[ + 'class' => 'dropdown-item', + 'href' => '#', + 'data-filter' => 'category', + 'data-pref' => 'all', + 'data-value' => 'all' + ]); + $catslist = html_writer::tag('li', $allcatslink); + foreach ($coursescats as $cat => $catlistitem) { + $catslist .= $catlistitem->$cat; + } + $data['categories'] = $catslist; + $data['categorypreference'] = $categorypreferenceplaceholder; + } return $this->render_from_template('block_myoverview/main', $data); } diff --git a/classes/webservice/ws_block_myoverview.php b/classes/webservice/ws_block_myoverview.php index 6799a5651..1c7ccba2e 100644 --- a/classes/webservice/ws_block_myoverview.php +++ b/classes/webservice/ws_block_myoverview.php @@ -59,6 +59,8 @@ public static function service_parameters() { VALUE_DEFAULT, null), 'progress' => new external_value(PARAM_TEXT, 'The courses completion progress', VALUE_DEFAULT, null), + 'catdata' => new external_value(PARAM_TEXT, 'The courses category', + VALUE_DEFAULT, null), ) ); } @@ -87,8 +89,10 @@ public static function service( string $customfieldvalue = null, string $searchvalue = null, string $yeardata = null, - string $progress = null + string $progress = null, + string $catdata = null ) { + global $DB; $params = self::validate_parameters(self::service_parameters(), array( @@ -100,7 +104,8 @@ public static function service( 'customfieldvalue' => $customfieldvalue, 'searchvalue' => $searchvalue, 'yeardata' => $yeardata, - 'progress' => $progress + 'progress' => $progress, + 'catdata' => $catdata, ) ); $mainFiltersResult = \core_course_external::get_enrolled_courses_by_timeline_classification( @@ -123,6 +128,20 @@ public static function service( } $mainFiltersResult["courses"] = $filteredbyyearcourses; } + if ($params['catdata'] != null && $params['catdata'] != "all") { + $filteredbycatcourses = []; + $categorydata = $DB->get_records('course_categories',null,'','id, name'); + $catdata = []; + foreach ($categorydata as $category) { + $catdata[$category->id] = $category->name; + } + foreach ($mainFiltersResult["courses"] as $course) { + if ($course->coursecategory == $catdata[$params['catdata']]) { + $filteredbycatcourses[] = $course; + } + } + $mainFiltersResult["courses"] = $filteredbycatcourses; + } if ($params['progress'] != "all") { $filteredbycompleted = []; diff --git a/lang/en/theme_snap.php b/lang/en/theme_snap.php index bb9b4e3b3..2a18fe249 100644 --- a/lang/en/theme_snap.php +++ b/lang/en/theme_snap.php @@ -496,6 +496,7 @@ $string['year'] = 'Year'; $string['homepage'] = 'Homepage'; $string['allyears'] = 'All years'; +$string['allcat'] = 'All Categories'; $string['courselink'] = 'Go to course'; $string['student'] = 'Student'; $string['showcourseinformation'] = 'Show course information'; diff --git a/lib.php b/lib.php index 4b801310d..cd5fa233b 100644 --- a/lib.php +++ b/lib.php @@ -577,5 +577,11 @@ function theme_snap_user_preferences(): array { 'default' => 'all', 'permissioncallback' => [core_user::class, 'is_current_user'], ], + 'snap_my_courses_category_user_preference' => [ + 'type' => PARAM_ALPHANUM, + 'null' => NULL_NOT_ALLOWED, + 'default' => 'all', + 'permissioncallback' => [core_user::class, 'is_current_user'], + ], ]; } diff --git a/templates/block_myoverview/courses-view.mustache b/templates/block_myoverview/courses-view.mustache index 21995a8e9..a9234822e 100644 --- a/templates/block_myoverview/courses-view.mustache +++ b/templates/block_myoverview/courses-view.mustache @@ -30,6 +30,7 @@ }}
block_myoverview/nav-sort-selector }} + {{> block_myoverview/nav-category-selector }} + {{#displaydropdown}} {{> block_myoverview/nav-display-selector }} {{/displaydropdown}} diff --git a/templates/block_myoverview/nav-category-selector.mustache b/templates/block_myoverview/nav-category-selector.mustache new file mode 100644 index 000000000..bed0fca8a --- /dev/null +++ b/templates/block_myoverview/nav-category-selector.mustache @@ -0,0 +1,34 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template block_myoverview/nav-category-selector + + This template renders category dropdown. + +}} +{{#categories}} + +{{/categories}} \ No newline at end of file diff --git a/tests/webservice_ws_block_myoverview.php b/tests/webservice_ws_block_myoverview.php index 19dad21bf..eeac00e2a 100644 --- a/tests/webservice_ws_block_myoverview.php +++ b/tests/webservice_ws_block_myoverview.php @@ -44,17 +44,27 @@ public function test_service() { $startdate = gmmktime('0', '0', '0', 10, 24, 2023); $enddate = gmmktime('0', '0', '0', 10, 24, 2024); - $course = $this->getDataGenerator()->create_course(['startdate' => $startdate, 'enddate' => $enddate]); $user = $this->getDataGenerator()->create_user(); - - $studentrole = $DB->get_record('role', array('shortname' => 'student')); + $categorycreated = $this->getDataGenerator()->create_category(['name' => 'test']); + $course2 = $this->getDataGenerator()->create_course([ + 'startdate' => $startdate, + 'enddate' => $enddate, + 'category' => $categorycreated->id]); + $studentrole = $DB->get_record('role', ['shortname' => 'student']); $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id); - + $this->getDataGenerator()->enrol_user($user->id, + $course2->id, + $studentrole->id); + // User not enrol in this course. + $this->getDataGenerator()->create_course([ + 'startdate' => $startdate, + 'enddate' => $enddate, + 'category' => $categorycreated->id]); $this->setUser($user); - + $usercourses = enrol_get_my_courses(); $classification = 'all'; $limit = 0; $offset = 0; @@ -64,6 +74,7 @@ public function test_service() { $searchvalue = null; $yeardata = '2022'; $progress = null; + $category = null; $result = ws_block_myoverview::service( $classification, @@ -74,7 +85,8 @@ public function test_service() { $customfieldvalue, $searchvalue, $yeardata, - $progress + $progress, + $category ); $this->assertEmpty($result["courses"]); @@ -88,6 +100,7 @@ public function test_service() { $searchvalue = null; $yeardata = '2024'; $progress = null; + $category = 'all'; $result = ws_block_myoverview::service( $classification, @@ -98,9 +111,36 @@ public function test_service() { $customfieldvalue, $searchvalue, $yeardata, - $progress + $progress, + $category ); + // All category filter selected, all courses should be displayed. + $this->assertEquals(count($usercourses), count($result["courses"])); + // Testing the category filter with an extra category. + $classification = 'all'; + $limit = 0; + $offset = 0; + $sort = 'fullname'; + $customfieldname = null; + $customfieldvalue = null; + $searchvalue = null; + $yeardata = '2024'; + $progress = null; + $category = $categorycreated->id; - $this->assertNotEmpty($result["courses"]); + $result = ws_block_myoverview::service( + $classification, + $limit, + $offset, + $sort, + $customfieldname, + $customfieldvalue, + $searchvalue, + $yeardata, + $progress, + $category + ); + // Only one category with the filter. + $this->assertEquals(1, count($result["courses"])); } } diff --git a/version.php b/version.php index 70e98fc0a..ee25fa552 100644 --- a/version.php +++ b/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2025060500; +$plugin->version = 2025060501; $plugin->requires = 2024100700; $plugin->release = '4.5.4'; $plugin->maturity = MATURITY_STABLE;