forked from EVOgeek/Vmeda.Online
		
	MOBILE-3594 sitehome: Course listing components of sitehome
This commit is contained in:
		
							parent
							
								
									97d1c93399
								
							
						
					
					
						commit
						0d66e66fdd
					
				
							
								
								
									
										44
									
								
								src/core/features/courses/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/features/courses/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { IonicModule } from '@ionic/angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| 
 | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| import { CorePipesModule } from '@pipes/pipes.module'; | ||||
| 
 | ||||
| import { CoreCoursesCourseListItemComponent } from './course-list-item/course-list-item'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         CoreCoursesCourseListItemComponent, | ||||
|     ], | ||||
|     imports: [ | ||||
|         CommonModule, | ||||
|         IonicModule, | ||||
|         TranslateModule.forChild(), | ||||
|         CoreComponentsModule, | ||||
|         CoreDirectivesModule, | ||||
|         CorePipesModule, | ||||
|     ], | ||||
|     providers: [ | ||||
|     ], | ||||
|     exports: [ | ||||
|         CoreCoursesCourseListItemComponent, | ||||
|     ], | ||||
| }) | ||||
| export class CoreCoursesComponentsModule {} | ||||
| @ -0,0 +1,14 @@ | ||||
| <ion-item class="ion-text-wrap" (click)="openCourse()" [class.item-disabled]="course.visible == 0" | ||||
| [title]="course.displayname || course.fullname" detail> | ||||
|     <ion-icon name="fas-graduation-cap" slot="start"></ion-icon> | ||||
|     <ion-label> | ||||
|         <h2> | ||||
|             <core-format-text [text]="course.displayname || course.fullname" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|             </core-format-text> | ||||
|         </h2> | ||||
|     </ion-label> | ||||
|     <ng-container *ngIf="!isEnrolled"> | ||||
|         <ion-icon *ngFor="let icon of icons" color="dark" size="small" | ||||
|         [name]="icon.icon" [attr.aria-label]="icon.label | translate" slot="end"></ion-icon> | ||||
|     </ng-container> | ||||
| </ion-item> | ||||
| @ -0,0 +1,105 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, Input, OnInit } from '@angular/core'; | ||||
| import { NavController } from '@ionic/angular'; | ||||
| import { CoreCourseHelper } from '@features/course/services/course.helper'; | ||||
| import { CoreCourses, CoreCourseSearchedData } from '@features/courses/services/courses'; | ||||
| 
 | ||||
| /** | ||||
|  * This directive is meant to display an item for a list of courses. | ||||
|  * | ||||
|  * Example usage: | ||||
|  * | ||||
|  * <core-courses-course-list-item [course]="course"></core-courses-course-list-item> | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-courses-course-list-item', | ||||
|     templateUrl: 'core-courses-course-list-item.html', | ||||
| }) | ||||
| export class CoreCoursesCourseListItemComponent implements OnInit { | ||||
| 
 | ||||
|     @Input() course!: CoreCourseSearchedData; // The course to render.
 | ||||
| 
 | ||||
|     icons: CoreCoursesEnrolmentIcons[] = []; | ||||
|     isEnrolled = false; | ||||
| 
 | ||||
|     constructor( | ||||
|         protected navCtrl: NavController, | ||||
|     ) { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         // Check if the user is enrolled in the course.
 | ||||
|         try { | ||||
|             await CoreCourses.instance.getUserCourse(this.course.id); | ||||
| 
 | ||||
|             this.isEnrolled = true; | ||||
|         } catch { | ||||
|             this.isEnrolled = false; | ||||
|             this.icons = []; | ||||
| 
 | ||||
|             this.course.enrollmentmethods.forEach((instance) => { | ||||
|                 if (instance === 'self') { | ||||
|                     this.icons.push({ | ||||
|                         label: 'core.courses.selfenrolment', | ||||
|                         icon: 'fas-key', | ||||
|                     }); | ||||
|                 } else if (instance === 'guest') { | ||||
|                     this.icons.push({ | ||||
|                         label: 'core.courses.allowguests', | ||||
|                         icon: 'fas-unlock', | ||||
|                     }); | ||||
|                 } else if (instance === 'paypal') { | ||||
|                     this.icons.push({ | ||||
|                         label: 'core.courses.paypalaccepted', | ||||
|                         icon: 'fab-paypal', | ||||
|                     }); | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             if (this.icons.length == 0) { | ||||
|                 this.icons.push({ | ||||
|                     label: 'core.courses.notenrollable', | ||||
|                     icon: 'fas-lock', | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Open a course. | ||||
|      * | ||||
|      * @param course The course to open. | ||||
|      */ | ||||
|     openCourse(): void { | ||||
|         if (this.isEnrolled) { | ||||
|             CoreCourseHelper.instance.openCourse(this.course); | ||||
|         } else { | ||||
|             this.navCtrl.navigateForward('/courses/preview', { queryParams: { course: this.course } }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Enrolment icons to show on the list with a label. | ||||
|  */ | ||||
| export type CoreCoursesEnrolmentIcons = { | ||||
|     label: string; | ||||
|     icon: string; | ||||
| }; | ||||
| @ -13,12 +13,12 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { Routes } from '@angular/router'; | ||||
| import { RouterModule, Routes } from '@angular/router'; | ||||
| import { CoreHomeRoutingModule } from '../mainmenu/pages/home/home-routing.module'; | ||||
| import { CoreHomeDelegate } from '../mainmenu/services/home.delegate'; | ||||
| import { CoreDashboardHomeHandler } from './services/handlers/dashboard.home'; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
| const homeRoutes: Routes = [ | ||||
|     { | ||||
|         path: 'dashboard', | ||||
|         loadChildren: () => | ||||
| @ -26,9 +26,50 @@ const routes: Routes = [ | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: 'courses', | ||||
|         children: [ | ||||
|             { | ||||
|                 path: '', | ||||
|                 redirectTo: 'all', | ||||
|                 pathMatch: 'full', | ||||
|             }, | ||||
|             { | ||||
|                 path: 'categories', | ||||
|                 redirectTo: 'categories/root', // Fake "id".
 | ||||
|                 pathMatch: 'full', | ||||
|             }, | ||||
|             { | ||||
|                 path: 'categories/:id', | ||||
|                 loadChildren: () => | ||||
|                     import('@features/courses/pages/categories/categories.page.module').then(m => m.CoreCoursesCategoriesPageModule), | ||||
|             }, | ||||
|             { | ||||
|                 path: 'all', | ||||
|                 loadChildren: () => | ||||
|                     import('@features/courses/pages/available-courses/available-courses.page.module') | ||||
|                         .then(m => m.CoreCoursesAvailableCoursesPageModule), | ||||
|             }, | ||||
|             { | ||||
|                 path: 'search', | ||||
|                 loadChildren: () => | ||||
|                     import('@features/courses/pages/search/search.page.module') | ||||
|                         .then(m => m.CoreCoursesSearchPageModule), | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     imports: [CoreHomeRoutingModule.forChild(routes)], | ||||
|     exports: [CoreHomeRoutingModule], | ||||
|     imports: [ | ||||
|         CoreHomeRoutingModule.forChild(homeRoutes), | ||||
|         RouterModule.forChild(routes), | ||||
|     ], | ||||
|     exports: [ | ||||
|         CoreHomeRoutingModule, | ||||
|         RouterModule, | ||||
|     ], | ||||
|     providers: [ | ||||
|         CoreDashboardHomeHandler, | ||||
|     ], | ||||
|  | ||||
| @ -0,0 +1,19 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <ion-buttons slot="start"> | ||||
|             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> | ||||
|         </ion-buttons> | ||||
|         <ion-title>{{ 'core.courses.availablecourses' | translate }}</ion-title> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-refresher slot="fixed" [disabled]="!coursesLoaded" (ionRefresh)="refreshCourses($event)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
|     <core-loading [hideUntil]="coursesLoaded"> | ||||
|         <ng-container *ngIf="courses.length > 0"> | ||||
|             <core-courses-course-list-item *ngFor="let course of courses" [course]="course"></core-courses-course-list-item> | ||||
|         </ng-container> | ||||
|         <core-empty-box *ngIf="!courses.length" icon="fas-graduation-cap" [message]="'core.courses.nocourses' | translate"></core-empty-box> | ||||
|     </core-loading> | ||||
| </ion-content> | ||||
| @ -0,0 +1,50 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { RouterModule, Routes } from '@angular/router'; | ||||
| import { IonicModule } from '@ionic/angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| 
 | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| import { CoreCoursesComponentsModule } from '../../components/components.module'; | ||||
| 
 | ||||
| import { CoreCoursesAvailableCoursesPage } from './available-courses.page'; | ||||
| 
 | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: '', | ||||
|         component: CoreCoursesAvailableCoursesPage, | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     imports: [ | ||||
|         RouterModule.forChild(routes), | ||||
|         CommonModule, | ||||
|         IonicModule, | ||||
|         TranslateModule.forChild(), | ||||
|         CoreComponentsModule, | ||||
|         CoreDirectivesModule, | ||||
|         CoreCoursesComponentsModule, | ||||
|     ], | ||||
|     declarations: [ | ||||
|         CoreCoursesAvailableCoursesPage, | ||||
|     ], | ||||
|     exports: [RouterModule], | ||||
| }) | ||||
| export class CoreCoursesAvailableCoursesPageModule { } | ||||
| @ -0,0 +1,78 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, OnInit } from '@angular/core'; | ||||
| import { IonRefresher } from '@ionic/angular'; | ||||
| 
 | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreCourses, CoreCourseSearchedData } from '../../services/courses'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays available courses in current site. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'page-core-courses-available-courses', | ||||
|     templateUrl: 'available-courses.html', | ||||
| }) | ||||
| export class CoreCoursesAvailableCoursesPage implements OnInit { | ||||
| 
 | ||||
|     courses: CoreCourseSearchedData[] = []; | ||||
|     coursesLoaded = false; | ||||
| 
 | ||||
|     /** | ||||
|      * View loaded. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.loadCourses().finally(() => { | ||||
|             this.coursesLoaded = true; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load the courses. | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async loadCourses(): Promise<void> { | ||||
|         const frontpageCourseId = CoreSites.instance.getCurrentSite()!.getSiteHomeId(); | ||||
| 
 | ||||
|         try { | ||||
|             const courses = await CoreCourses.instance.getCoursesByField(); | ||||
| 
 | ||||
|             this.courses = courses.filter((course) => course.id != frontpageCourseId); | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.instance.showErrorModalDefault(error, 'core.courses.errorloadcourses', true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Refresh the courses. | ||||
|      * | ||||
|      * @param refresher Refresher. | ||||
|      */ | ||||
|     refreshCourses(refresher: CustomEvent<IonRefresher>): void { | ||||
|         const promises: Promise<void>[] = []; | ||||
| 
 | ||||
|         promises.push(CoreCourses.instance.invalidateUserCourses()); | ||||
|         promises.push(CoreCourses.instance.invalidateCoursesByField()); | ||||
| 
 | ||||
|         Promise.all(promises).finally(() => { | ||||
|             this.loadCourses().finally(() => { | ||||
|                 refresher?.detail.complete(); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										67
									
								
								src/core/features/courses/pages/categories/categories.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/core/features/courses/pages/categories/categories.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <ion-buttons slot="start"> | ||||
|             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> | ||||
|         </ion-buttons> | ||||
|         <ion-title> | ||||
|             <core-format-text [text]="title" contextLevel="coursecat" [contextInstanceId]="currentCategory && currentCategory!.id"> | ||||
|             </core-format-text> | ||||
|         </ion-title> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-refresher slot="fixed" [disabled]="!categoriesLoaded" (ionRefresh)="refreshCategories($event)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
|     <core-loading [hideUntil]="categoriesLoaded"> | ||||
|         <ion-item *ngIf="currentCategory" class="ion-text-wrap"> | ||||
|             <ion-icon name="fas-folder" slot="start"></ion-icon> | ||||
|             <ion-label> | ||||
|                 <h2> | ||||
|                     <core-format-text [text]="currentCategory!.name" contextLevel="coursecat" | ||||
|                     [contextInstanceId]="currentCategory!.id"></core-format-text> | ||||
|                 </h2> | ||||
|             </ion-label> | ||||
|         </ion-item> | ||||
|         <ion-item class="ion-text-wrap" *ngIf="currentCategory && currentCategory!.description"> | ||||
|             <ion-label> | ||||
|                 <h2> | ||||
|                     <core-format-text [text]="currentCategory!.description" maxHeight="60" contextLevel="coursecat" | ||||
|             [contextInstanceId]="currentCategory!.id"></core-format-text> | ||||
|                 </h2> | ||||
|             </ion-label> | ||||
|         </ion-item> | ||||
| 
 | ||||
|         <div *ngIf="categories.length > 0"> | ||||
|             <ion-item-divider> | ||||
|                 <ion-label> | ||||
|                     <h2>{{ 'core.courses.categories' | translate }}</h2> | ||||
|                 </ion-label> | ||||
|             </ion-item-divider> | ||||
|             <section *ngFor="let category of categories"> | ||||
|                 <ion-item class="ion-text-wrap" router-direction="forward" [routerLink]="['/courses/categories', category.id]" | ||||
|                 [title]="category.name" detail> | ||||
|                     <ion-icon name="fas-folder" slot="start"></ion-icon> | ||||
|                     <ion-label> | ||||
|                         <h2> | ||||
|                             <core-format-text [text]="category.name" contextLevel="coursecat" [contextInstanceId]="category.id"> | ||||
|                             </core-format-text> | ||||
|                         </h2> | ||||
|                     </ion-label> | ||||
|                     <ion-badge slot="end" *ngIf="category.coursecount > 0" color="light">{{category.coursecount}}</ion-badge> | ||||
|                 </ion-item> | ||||
|             </section> | ||||
|         </div> | ||||
| 
 | ||||
|         <div *ngIf="courses.length > 0"> | ||||
|             <ion-item-divider> | ||||
|                 <ion-label> | ||||
|                     <h2>{{ 'core.courses.courses' | translate }}</h2> | ||||
|                 </ion-label> | ||||
|             </ion-item-divider> | ||||
|             <core-courses-course-list-item *ngFor="let course of courses" [course]="course"></core-courses-course-list-item> | ||||
|         </div> | ||||
|         <core-empty-box *ngIf="!categories.length && !courses.length" icon="ionic" [message]="'core.courses.nocoursesyet' | translate"> | ||||
|         </core-empty-box> | ||||
|     </core-loading> | ||||
| </ion-content> | ||||
| @ -0,0 +1,50 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { RouterModule, Routes } from '@angular/router'; | ||||
| import { IonicModule } from '@ionic/angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| 
 | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| import { CoreCoursesComponentsModule } from '../../components/components.module'; | ||||
| 
 | ||||
| import { CoreCoursesCategoriesPage } from './categories.page'; | ||||
| 
 | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: '', | ||||
|         component: CoreCoursesCategoriesPage, | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     imports: [ | ||||
|         RouterModule.forChild(routes), | ||||
|         CommonModule, | ||||
|         IonicModule, | ||||
|         TranslateModule.forChild(), | ||||
|         CoreComponentsModule, | ||||
|         CoreDirectivesModule, | ||||
|         CoreCoursesComponentsModule, | ||||
|     ], | ||||
|     declarations: [ | ||||
|         CoreCoursesCategoriesPage, | ||||
|     ], | ||||
|     exports: [RouterModule], | ||||
| }) | ||||
| export class CoreCoursesCategoriesPageModule { } | ||||
							
								
								
									
										123
									
								
								src/core/features/courses/pages/categories/categories.page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/core/features/courses/pages/categories/categories.page.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, OnInit } from '@angular/core'; | ||||
| import { IonRefresher, NavController } from '@ionic/angular'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreCategoryData, CoreCourses, CoreCourseSearchedData } from '../../services/courses'; | ||||
| import { Translate } from '@singletons/core.singletons'; | ||||
| import { ActivatedRoute } from '@angular/router'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays a list of categories and the courses in the current category if any. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'page-core-courses-categories', | ||||
|     templateUrl: 'categories.html', | ||||
| }) | ||||
| export class CoreCoursesCategoriesPage implements OnInit { | ||||
| 
 | ||||
|     title: string; | ||||
|     currentCategory?: CoreCategoryData; | ||||
|     categories: CoreCategoryData[] = []; | ||||
|     courses: CoreCourseSearchedData[] = []; | ||||
|     categoriesLoaded = false; | ||||
| 
 | ||||
|     protected categoryId = 0; | ||||
| 
 | ||||
|     constructor( | ||||
|         protected navCtrl: NavController, | ||||
|         protected route: ActivatedRoute, | ||||
|     ) { | ||||
|         this.title = Translate.instance.instant('core.courses.categories'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * View loaded. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.categoryId = parseInt(this.route.snapshot.params['id'], 0) || 0; | ||||
| 
 | ||||
|         this.fetchCategories().finally(() => { | ||||
|             this.categoriesLoaded = true; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetch the categories. | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async fetchCategories(): Promise<void> { | ||||
|         try{ | ||||
|             const categories: CoreCategoryData[] = await CoreCourses.instance.getCategories(this.categoryId, true); | ||||
| 
 | ||||
|             this.currentCategory = undefined; | ||||
| 
 | ||||
|             const index = categories.findIndex((category) => category.id == this.categoryId); | ||||
| 
 | ||||
|             if (index >= 0) { | ||||
|                 this.currentCategory = categories[index]; | ||||
|                 // Delete current Category to avoid problems with the formatTree.
 | ||||
|                 delete categories[index]; | ||||
|             } | ||||
| 
 | ||||
|             // Sort by depth and sortorder to avoid problems formatting Tree.
 | ||||
|             categories.sort((a, b) => { | ||||
|                 if (a.depth == b.depth) { | ||||
|                     return (a.sortorder > b.sortorder) ? 1 : ((b.sortorder > a.sortorder) ? -1 : 0); | ||||
|                 } | ||||
| 
 | ||||
|                 return a.depth > b.depth ? 1 : -1; | ||||
|             }); | ||||
| 
 | ||||
|             this.categories = CoreUtils.instance.formatTree(categories, 'parent', 'id', this.categoryId); | ||||
| 
 | ||||
|             if (this.currentCategory) { | ||||
|                 this.title = this.currentCategory.name; | ||||
| 
 | ||||
|                 try { | ||||
|                     this.courses = await CoreCourses.instance.getCoursesByField('category', this.categoryId); | ||||
|                 } catch (error) { | ||||
|                     CoreDomUtils.instance.showErrorModalDefault(error, 'core.courses.errorloadcourses', true); | ||||
|                 } | ||||
|             } | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.instance.showErrorModalDefault(error, 'core.courses.errorloadcategories', true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Refresh the categories. | ||||
|      * | ||||
|      * @param refresher Refresher. | ||||
|      */ | ||||
|     refreshCategories(refresher?: CustomEvent<IonRefresher>): void { | ||||
|         const promises: Promise<void>[] = []; | ||||
| 
 | ||||
|         promises.push(CoreCourses.instance.invalidateUserCourses()); | ||||
|         promises.push(CoreCourses.instance.invalidateCategories(this.categoryId, true)); | ||||
|         promises.push(CoreCourses.instance.invalidateCoursesByField('category', this.categoryId)); | ||||
|         promises.push(CoreSites.instance.getCurrentSite()!.invalidateConfig()); | ||||
| 
 | ||||
|         Promise.all(promises).finally(() => { | ||||
|             this.fetchCategories().finally(() => { | ||||
|                 refresher?.detail.complete(); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										24
									
								
								src/core/features/courses/pages/search/search.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/core/features/courses/pages/search/search.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <ion-buttons slot="start"> | ||||
|             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> | ||||
|         </ion-buttons> | ||||
|         <ion-title>{{ 'core.courses.searchcourses' | translate }}</ion-title> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <core-search-box (onSubmit)="search($event)" (onClear)="clearSearch($event)" | ||||
|     [placeholder]="'core.courses.search' | translate" [searchLabel]="'core.courses.search' | translate" autoFocus="true" | ||||
|     searchArea="CoreCoursesSearch"></core-search-box> | ||||
| 
 | ||||
|     <ng-container *ngIf="total > 0"> | ||||
|         <ion-item-divider> | ||||
|             <ion-label><h2>{{ 'core.courses.totalcoursesearchresults' | translate:{$a: total} }}</h2></ion-label> | ||||
|         </ion-item-divider> | ||||
|         <core-courses-course-list-item *ngFor="let course of courses" [course]="course"></core-courses-course-list-item> | ||||
|         <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreResults($event)" [error]="loadMoreError"> | ||||
|         </core-infinite-loading> | ||||
|     </ng-container> | ||||
|     <core-empty-box *ngIf="total == 0" icon="search" [message]="'core.courses.nosearchresults' | translate"></core-empty-box> | ||||
| </ion-content> | ||||
| 
 | ||||
							
								
								
									
										52
									
								
								src/core/features/courses/pages/search/search.page.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/core/features/courses/pages/search/search.page.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { RouterModule, Routes } from '@angular/router'; | ||||
| import { IonicModule } from '@ionic/angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| 
 | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| import { CoreCoursesComponentsModule } from '../../components/components.module'; | ||||
| import { CoreSearchComponentsModule } from '@features/search/components/components.module'; | ||||
| 
 | ||||
| import { CoreCoursesSearchPage } from './search.page'; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: '', | ||||
|         component: CoreCoursesSearchPage, | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     imports: [ | ||||
|         RouterModule.forChild(routes), | ||||
|         CommonModule, | ||||
|         IonicModule, | ||||
|         TranslateModule.forChild(), | ||||
|         CoreComponentsModule, | ||||
|         CoreDirectivesModule, | ||||
|         CoreCoursesComponentsModule, | ||||
|         CoreSearchComponentsModule, | ||||
|     ], | ||||
|     declarations: [ | ||||
|         CoreCoursesSearchPage, | ||||
|     ], | ||||
|     exports: [RouterModule], | ||||
| }) | ||||
| export class CoreCoursesSearchPageModule { } | ||||
| 
 | ||||
							
								
								
									
										100
									
								
								src/core/features/courses/pages/search/search.page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/core/features/courses/pages/search/search.page.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component } from '@angular/core'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreCourseBasicSearchedData, CoreCourses } from '../../services/courses'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that allows searching for courses. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'page-core-courses-search', | ||||
|     templateUrl: 'search.html', | ||||
| }) | ||||
| export class CoreCoursesSearchPage { | ||||
| 
 | ||||
|     total = 0; | ||||
|     courses: CoreCourseBasicSearchedData[] = []; | ||||
|     canLoadMore = false; | ||||
|     loadMoreError = false; | ||||
| 
 | ||||
|     protected page = 0; | ||||
|     protected currentSearch = ''; | ||||
| 
 | ||||
|     /** | ||||
|      * Search a new text. | ||||
|      * | ||||
|      * @param text The text to search. | ||||
|      */ | ||||
|     async search(text: string): Promise<void> { | ||||
|         this.currentSearch = text; | ||||
|         this.courses = []; | ||||
|         this.page = 0; | ||||
|         this.total = 0; | ||||
| 
 | ||||
|         const modal = await CoreDomUtils.instance.showModalLoading('core.searching', true); | ||||
|         this.searchCourses().finally(() => { | ||||
|             modal.dismiss(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Clear search box. | ||||
|      */ | ||||
|     clearSearch(): void { | ||||
|         this.currentSearch = ''; | ||||
|         this.courses = []; | ||||
|         this.page = 0; | ||||
|         this.total = 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load more results. | ||||
|      * | ||||
|      * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. | ||||
|      */ | ||||
|     loadMoreResults(infiniteComplete?: () => void ): void { | ||||
|         this.searchCourses().finally(() => { | ||||
|             infiniteComplete && infiniteComplete(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Search courses or load the next page of current search. | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async searchCourses(): Promise<void> { | ||||
|         this.loadMoreError = false; | ||||
| 
 | ||||
|         try { | ||||
|             const response = await CoreCourses.instance.search(this.currentSearch, this.page); | ||||
| 
 | ||||
|             if (this.page === 0) { | ||||
|                 this.courses = response.courses; | ||||
|             } else { | ||||
|                 this.courses = this.courses.concat(response.courses); | ||||
|             } | ||||
|             this.total = response.total; | ||||
| 
 | ||||
|             this.page++; | ||||
|             this.canLoadMore = this.courses.length < this.total; | ||||
|         } catch (error) { | ||||
|             this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
 | ||||
|             CoreDomUtils.instance.showErrorModalDefault(error, 'core.courses.errorsearching', true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -39,7 +39,9 @@ | ||||
|                         </ng-container> | ||||
|                     </ng-container> | ||||
|                 </ion-list> | ||||
|             <core-empty-box *ngIf="!hasContent" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box> | ||||
|             <core-empty-box *ngIf="!hasContent" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"> | ||||
| 
 | ||||
|             </core-empty-box> | ||||
|         </core-loading> | ||||
|     <!-- @todo </core-block-course-blocks> --> | ||||
| </ion-content> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user