forked from CIT/Vmeda.Online
		
	MOBILE-3641 feedback: Migrate other pages
This commit is contained in:
		
							parent
							
								
									77966d6fb4
								
							
						
					
					
						commit
						66993dc231
					
				@ -17,8 +17,11 @@ import { RouterModule, Routes } from '@angular/router';
 | 
				
			|||||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
					import { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
import { AddonModFeedbackComponentsModule } from './components/components.module';
 | 
					import { AddonModFeedbackComponentsModule } from './components/components.module';
 | 
				
			||||||
import { AddonModFeedbackIndexPage } from './pages/index/index';
 | 
					import { AddonModFeedbackIndexPage } from './pages/index/index';
 | 
				
			||||||
 | 
					import { AddonModFeedbackRespondentsPage } from './pages/respondents/respondents';
 | 
				
			||||||
 | 
					import { conditionalRoutes } from '@/app/app-routing.module';
 | 
				
			||||||
 | 
					import { CoreScreen } from '@services/screen';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const routes: Routes = [
 | 
					const commonRoutes: Routes = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        path: ':courseId/:cmId',
 | 
					        path: ':courseId/:cmId',
 | 
				
			||||||
        component: AddonModFeedbackIndexPage,
 | 
					        component: AddonModFeedbackIndexPage,
 | 
				
			||||||
@ -27,6 +30,42 @@ const routes: Routes = [
 | 
				
			|||||||
        path: ':courseId/:cmId/form',
 | 
					        path: ':courseId/:cmId/form',
 | 
				
			||||||
        loadChildren: () => import('./pages/form/form.module').then(m => m.AddonModFeedbackFormPageModule),
 | 
					        loadChildren: () => import('./pages/form/form.module').then(m => m.AddonModFeedbackFormPageModule),
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId/nonrespondents',
 | 
				
			||||||
 | 
					        loadChildren: () => import('./pages/nonrespondents/nonrespondents.module')
 | 
				
			||||||
 | 
					            .then(m => m.AddonModFeedbackNonRespondentsPageModule),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mobileRoutes: Routes = [
 | 
				
			||||||
 | 
					    ...commonRoutes,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId/respondents',
 | 
				
			||||||
 | 
					        component: AddonModFeedbackRespondentsPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId/attempt/:attemptId',
 | 
				
			||||||
 | 
					        loadChildren: () => import('./pages/attempt/attempt.module').then(m => m.AddonModFeedbackAttemptPageModule),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tabletRoutes: Routes = [
 | 
				
			||||||
 | 
					    ...commonRoutes,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId/respondents',
 | 
				
			||||||
 | 
					        component: AddonModFeedbackRespondentsPage,
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                path: 'attempt/:attemptId',
 | 
				
			||||||
 | 
					                loadChildren: () => import('./pages/attempt/attempt.module').then(m => m.AddonModFeedbackAttemptPageModule),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    ...conditionalRoutes(mobileRoutes, () => CoreScreen.isMobile),
 | 
				
			||||||
 | 
					    ...conditionalRoutes(tabletRoutes, () => CoreScreen.isTablet),
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
@ -37,6 +76,7 @@ const routes: Routes = [
 | 
				
			|||||||
    ],
 | 
					    ],
 | 
				
			||||||
    declarations: [
 | 
					    declarations: [
 | 
				
			||||||
        AddonModFeedbackIndexPage,
 | 
					        AddonModFeedbackIndexPage,
 | 
				
			||||||
 | 
					        AddonModFeedbackRespondentsPage,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AddonModFeedbackLazyModule {}
 | 
					export class AddonModFeedbackLazyModule {}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										58
									
								
								src/addons/mod/feedback/pages/attempt/attempt.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/addons/mod/feedback/pages/attempt/attempt.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					<ion-header>
 | 
				
			||||||
 | 
					    <ion-toolbar>
 | 
				
			||||||
 | 
					        <ion-buttons slot="start">
 | 
				
			||||||
 | 
					            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					        <ion-title>
 | 
				
			||||||
 | 
					            <ng-container *ngIf="attempt">{{ attempt.fullname }}</ng-container>
 | 
				
			||||||
 | 
					            <ng-container *ngIf="anonAttempt">
 | 
				
			||||||
 | 
					                {{ 'addon.mod_feedback.response_nr' |translate }}: {{anonAttempt.number}}
 | 
				
			||||||
 | 
					            </ng-container>
 | 
				
			||||||
 | 
					        </ion-title>
 | 
				
			||||||
 | 
					    </ion-toolbar>
 | 
				
			||||||
 | 
					</ion-header>
 | 
				
			||||||
 | 
					<ion-content>
 | 
				
			||||||
 | 
					    <core-loading [hideUntil]="loaded">
 | 
				
			||||||
 | 
					        <ion-list class="ion-no-margin" *ngIf="attempt || anonAttempt">
 | 
				
			||||||
 | 
					            <ion-item *ngIf="attempt" class="ion-text-wrap" core-user-link [userId]="attempt.userid"
 | 
				
			||||||
 | 
					                [attr.aria-label]=" 'core.user.viewprofile' | translate" [courseId]="attempt.courseid" [title]="attempt.fullname">
 | 
				
			||||||
 | 
					                <core-user-avatar [user]="attempt" slot="start"></core-user-avatar>
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <h2>{{attempt.fullname}}</h2>
 | 
				
			||||||
 | 
					                    <p *ngIf="attempt.timemodified">{{attempt.timemodified * 1000 | coreFormatDate }}</p>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="anonAttempt">
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <h2>
 | 
				
			||||||
 | 
					                        {{ 'addon.mod_feedback.response_nr' |translate }}: {{anonAttempt.number}}
 | 
				
			||||||
 | 
					                        ({{ 'addon.mod_feedback.anonymous' |translate }})
 | 
				
			||||||
 | 
					                    </h2>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					            </ion-item >
 | 
				
			||||||
 | 
					            <ng-container *ngIf="items && items.length">
 | 
				
			||||||
 | 
					                <ng-container *ngFor="let item of items">
 | 
				
			||||||
 | 
					                    <ion-item-divider *ngIf="item.typ == 'pagebreak'">
 | 
				
			||||||
 | 
					                        <ion-label></ion-label>
 | 
				
			||||||
 | 
					                    </ion-item-divider>
 | 
				
			||||||
 | 
					                    <ion-item class="ion-text-wrap" *ngIf="item.typ != 'pagebreak'" [color]="item.dependitem > 0 ? 'light' : ''">
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            <h2 *ngIf="item.name" [core-mark-required]="item.required">
 | 
				
			||||||
 | 
					                                <span *ngIf="feedback!.autonumbering && item.itemnumber">{{item.itemnumber}}. </span>
 | 
				
			||||||
 | 
					                                <core-format-text  [component]="component" [componentId]="cmId" [text]="item.name"
 | 
				
			||||||
 | 
					                                    contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId">
 | 
				
			||||||
 | 
					                                </core-format-text>
 | 
				
			||||||
 | 
					                            </h2>
 | 
				
			||||||
 | 
					                            <p *ngIf="item.submittedValue">
 | 
				
			||||||
 | 
					                                <core-format-text [component]="component" [componentId]="cmId" [text]="item.submittedValue"
 | 
				
			||||||
 | 
					                                    contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId">
 | 
				
			||||||
 | 
					                                </core-format-text>
 | 
				
			||||||
 | 
					                            </p>
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					            </ng-container>
 | 
				
			||||||
 | 
					        </ion-list>
 | 
				
			||||||
 | 
					    </core-loading>
 | 
				
			||||||
 | 
					</ion-content>
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/addons/mod/feedback/pages/attempt/attempt.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/addons/mod/feedback/pages/attempt/attempt.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// (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 { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					import { RouterModule, Routes } from '@angular/router';
 | 
				
			||||||
 | 
					import { AddonModFeedbackAttemptPage } from './attempt';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: '',
 | 
				
			||||||
 | 
					        component: AddonModFeedbackAttemptPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    declarations: [
 | 
				
			||||||
 | 
					        AddonModFeedbackAttemptPage,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        RouterModule.forChild(routes),
 | 
				
			||||||
 | 
					        CoreSharedModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    exports: [RouterModule],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModFeedbackAttemptPageModule {}
 | 
				
			||||||
							
								
								
									
										125
									
								
								src/addons/mod/feedback/pages/attempt/attempt.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/addons/mod/feedback/pages/attempt/attempt.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,125 @@
 | 
				
			|||||||
 | 
					// (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 { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
 | 
					import { CoreTextUtils } from '@services/utils/text';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModFeedback,
 | 
				
			||||||
 | 
					    AddonModFeedbackProvider,
 | 
				
			||||||
 | 
					    AddonModFeedbackWSAnonAttempt,
 | 
				
			||||||
 | 
					    AddonModFeedbackWSAttempt,
 | 
				
			||||||
 | 
					    AddonModFeedbackWSFeedback,
 | 
				
			||||||
 | 
					} from '../../services/feedback';
 | 
				
			||||||
 | 
					import { AddonModFeedbackFormItem, AddonModFeedbackHelper } from '../../services/feedback-helper';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Page that displays a feedback attempt review.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'page-addon-mod-feedback-attempt',
 | 
				
			||||||
 | 
					    templateUrl: 'attempt.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModFeedbackAttemptPage implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected attemptId!: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cmId!: number;
 | 
				
			||||||
 | 
					    courseId!: number;
 | 
				
			||||||
 | 
					    feedback?: AddonModFeedbackWSFeedback;
 | 
				
			||||||
 | 
					    attempt?: AddonModFeedbackWSAttempt;
 | 
				
			||||||
 | 
					    anonAttempt?: AddonModFeedbackWSAnonAttempt;
 | 
				
			||||||
 | 
					    items: AddonModFeedbackAttemptItem[] = [];
 | 
				
			||||||
 | 
					    component = AddonModFeedbackProvider.COMPONENT;
 | 
				
			||||||
 | 
					    loaded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnInit(): void {
 | 
				
			||||||
 | 
					        this.cmId = CoreNavigator.getRouteNumberParam('cmId')!;
 | 
				
			||||||
 | 
					        this.courseId = CoreNavigator.getRouteNumberParam('courseId')!;
 | 
				
			||||||
 | 
					        this.attemptId = CoreNavigator.getRouteNumberParam('attemptId')!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.fetchData();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch all the data required for the view.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchData(): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.feedback = await AddonModFeedback.getFeedback(this.courseId, this.cmId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const attempt = await AddonModFeedback.getAttempt(this.feedback.id, this.attemptId, { cmId: this.cmId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (this.isAnonAttempt(attempt)) {
 | 
				
			||||||
 | 
					                this.anonAttempt = attempt;
 | 
				
			||||||
 | 
					                delete this.attempt;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                this.attempt = attempt;
 | 
				
			||||||
 | 
					                delete this.anonAttempt;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const items = await AddonModFeedback.getItems(this.feedback.id, { cmId: this.cmId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add responses and format items.
 | 
				
			||||||
 | 
					            this.items = <AddonModFeedbackAttemptItem[]> items.items.map((item) => {
 | 
				
			||||||
 | 
					                const formItem = AddonModFeedbackHelper.getItemForm(item, true);
 | 
				
			||||||
 | 
					                if (!formItem) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const attemptItem = <AddonModFeedbackAttemptItem> formItem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (item.typ == 'label') {
 | 
				
			||||||
 | 
					                    attemptItem.submittedValue = CoreTextUtils.replacePluginfileUrls(item.presentation, item.itemfiles);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    for (const x in attempt.responses) {
 | 
				
			||||||
 | 
					                        if (attempt.responses[x].id == item.id) {
 | 
				
			||||||
 | 
					                            attemptItem.submittedValue = attempt.responses[x].printval;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return attemptItem;
 | 
				
			||||||
 | 
					            }).filter((itemData) => itemData); // Filter items with errors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (message) {
 | 
				
			||||||
 | 
					            // Some call failed on fetch, go back.
 | 
				
			||||||
 | 
					            CoreDomUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
 | 
				
			||||||
 | 
					            CoreNavigator.back();
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.loaded = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if an attempt is anonymous or not.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param attempt Attempt to check.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    isAnonAttempt(attempt: AddonModFeedbackWSAttempt | AddonModFeedbackWSAnonAttempt): attempt is AddonModFeedbackWSAnonAttempt {
 | 
				
			||||||
 | 
					        return !('fullname' in attempt);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AddonModFeedbackAttemptItem = AddonModFeedbackFormItem & {
 | 
				
			||||||
 | 
					    submittedValue?: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					<ion-header>
 | 
				
			||||||
 | 
					    <ion-toolbar>
 | 
				
			||||||
 | 
					        <ion-buttons slot="start">
 | 
				
			||||||
 | 
					            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					        <ion-title>{{ 'addon.mod_feedback.responses' |translate }}</ion-title>
 | 
				
			||||||
 | 
					    </ion-toolbar>
 | 
				
			||||||
 | 
					</ion-header>
 | 
				
			||||||
 | 
					<ion-content>
 | 
				
			||||||
 | 
					    <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshFeedback($event.target)">
 | 
				
			||||||
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
				
			||||||
 | 
					    </ion-refresher>
 | 
				
			||||||
 | 
					    <core-loading [hideUntil]="loaded">
 | 
				
			||||||
 | 
					        <ion-list class="ion-no-margin">
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
 | 
				
			||||||
 | 
					                <ion-label id="addon-feedback-groupslabel">
 | 
				
			||||||
 | 
					                    <ng-container *ngIf="groupInfo.separateGroups">{{'core.groupsseparate' | translate }}</ng-container>
 | 
				
			||||||
 | 
					                    <ng-container *ngIf="groupInfo.visibleGroups">{{'core.groupsvisible' | translate }}</ng-container>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					                <ion-select [(ngModel)]="selectedGroup" (ionChange)="loadAttempts(selectedGroup)"
 | 
				
			||||||
 | 
					                    aria-labelledby="addon-feedback-groupslabel" interface="action-sheet">
 | 
				
			||||||
 | 
					                    <ion-select-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">
 | 
				
			||||||
 | 
					                        {{groupOpt.name}}
 | 
				
			||||||
 | 
					                    </ion-select-option>
 | 
				
			||||||
 | 
					                </ion-select>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <ion-item-divider>
 | 
				
			||||||
 | 
					                <ion-label>{{ 'addon.mod_feedback.non_respondents_students' | translate : {$a: total } }}</ion-label>
 | 
				
			||||||
 | 
					            </ion-item-divider>
 | 
				
			||||||
 | 
					            <ng-container *ngIf="total > 0">
 | 
				
			||||||
 | 
					                <ion-item *ngFor="let user of users" class="ion-text-wrap">
 | 
				
			||||||
 | 
					                    <core-user-avatar [user]="user" slot="start"></core-user-avatar>
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <h2>{{ user.fullname }}</h2>
 | 
				
			||||||
 | 
					                        <p>
 | 
				
			||||||
 | 
					                            <ion-badge color="success" *ngIf="user.started">
 | 
				
			||||||
 | 
					                                {{ 'addon.mod_feedback.started' | translate}}
 | 
				
			||||||
 | 
					                            </ion-badge>
 | 
				
			||||||
 | 
					                            <ion-badge color="danger" *ngIf="!user.started">
 | 
				
			||||||
 | 
					                                {{ 'addon.mod_feedback.not_started' | translate}}
 | 
				
			||||||
 | 
					                            </ion-badge>
 | 
				
			||||||
 | 
					                        </p>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					            </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <core-infinite-loading [enabled]="canLoadMore" (action)="loadAttempts(undefined, $event)" [error]="loadMoreError">
 | 
				
			||||||
 | 
					            </core-infinite-loading>
 | 
				
			||||||
 | 
					        </ion-list>
 | 
				
			||||||
 | 
					    </core-loading>
 | 
				
			||||||
 | 
					</ion-content>
 | 
				
			||||||
@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// (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 { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					import { RouterModule, Routes } from '@angular/router';
 | 
				
			||||||
 | 
					import { AddonModFeedbackNonRespondentsPage } from './nonrespondents';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: '',
 | 
				
			||||||
 | 
					        component: AddonModFeedbackNonRespondentsPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    declarations: [
 | 
				
			||||||
 | 
					        AddonModFeedbackNonRespondentsPage,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        RouterModule.forChild(routes),
 | 
				
			||||||
 | 
					        CoreSharedModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    exports: [RouterModule],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModFeedbackNonRespondentsPageModule {}
 | 
				
			||||||
							
								
								
									
										165
									
								
								src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								src/addons/mod/feedback/pages/nonrespondents/nonrespondents.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					// (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 { CoreGroupInfo, CoreGroups } from '@services/groups';
 | 
				
			||||||
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { AddonModFeedback, AddonModFeedbackWSFeedback } from '../../services/feedback';
 | 
				
			||||||
 | 
					import { AddonModFeedbackHelper, AddonModFeedbackNonRespondent } from '../../services/feedback-helper';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Page that displays feedback non respondents.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'page-addon-mod-feedback-nonrespondents',
 | 
				
			||||||
 | 
					    templateUrl: 'nonrespondents.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModFeedbackNonRespondentsPage implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected cmId!: number;
 | 
				
			||||||
 | 
					    protected courseId!: number;
 | 
				
			||||||
 | 
					    protected feedback?: AddonModFeedbackWSFeedback;
 | 
				
			||||||
 | 
					    protected page = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    selectedGroup!: number;
 | 
				
			||||||
 | 
					    groupInfo?: CoreGroupInfo;
 | 
				
			||||||
 | 
					    users: AddonModFeedbackNonRespondent[] = [];
 | 
				
			||||||
 | 
					    total = 0;
 | 
				
			||||||
 | 
					    canLoadMore = false;
 | 
				
			||||||
 | 
					    loaded = false;
 | 
				
			||||||
 | 
					    loadMoreError = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnInit(): void {
 | 
				
			||||||
 | 
					        this.cmId = CoreNavigator.getRouteNumberParam('cmId')!;
 | 
				
			||||||
 | 
					        this.courseId = CoreNavigator.getRouteNumberParam('courseId')!;
 | 
				
			||||||
 | 
					        this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.fetchData();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch all the data required for the view.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresh Empty events array first.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchData(refresh: boolean = false): Promise<void> {
 | 
				
			||||||
 | 
					        this.page = 0;
 | 
				
			||||||
 | 
					        this.total = 0;
 | 
				
			||||||
 | 
					        this.users = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.feedback = await AddonModFeedback.getFeedback(this.courseId, this.cmId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.groupInfo = await CoreGroups.getActivityGroupInfo(this.cmId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.selectedGroup = CoreGroups.validateGroupId(this.selectedGroup, this.groupInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await this.loadGroupUsers(this.selectedGroup);
 | 
				
			||||||
 | 
					        } catch (message) {
 | 
				
			||||||
 | 
					            CoreDomUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!refresh) {
 | 
				
			||||||
 | 
					                // Some call failed on first fetch, go back.
 | 
				
			||||||
 | 
					                CoreNavigator.back();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load Group responses.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param groupId If defined it will change group if not, it will load more users for the same group.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async loadGroupUsers(groupId?: number): Promise<void> {
 | 
				
			||||||
 | 
					        this.loadMoreError = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (typeof groupId == 'undefined') {
 | 
				
			||||||
 | 
					            this.page++;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.selectedGroup = groupId;
 | 
				
			||||||
 | 
					            this.page = 0;
 | 
				
			||||||
 | 
					            this.total = 0;
 | 
				
			||||||
 | 
					            this.users = [];
 | 
				
			||||||
 | 
					            this.loaded = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const response = await AddonModFeedbackHelper.getNonRespondents(this.feedback!.id, {
 | 
				
			||||||
 | 
					                groupId: this.selectedGroup,
 | 
				
			||||||
 | 
					                page: this.page,
 | 
				
			||||||
 | 
					                cmId: this.cmId,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.total = response.total;
 | 
				
			||||||
 | 
					            if (this.users.length < response.total) {
 | 
				
			||||||
 | 
					                this.users = this.users.concat(response.users);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.canLoadMore = this.users.length < response.total;
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            this.loadMoreError = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw error;
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.loaded = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Change selected group or load more users.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param groupId Group ID selected. If not defined, it will load more users.
 | 
				
			||||||
 | 
					     * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async loadAttempts(groupId?: number, infiniteComplete?: () => void): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await this.loadGroupUsers(groupId);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            infiniteComplete && infiniteComplete();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Refresh the attempts.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresher Refresher.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async refreshFeedback(refresher: IonRefresher): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            promises.push(CoreGroups.invalidateActivityGroupInfo(this.cmId));
 | 
				
			||||||
 | 
					            if (this.feedback) {
 | 
				
			||||||
 | 
					                promises.push(AddonModFeedback.invalidateNonRespondentsData(this.feedback.id));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await CoreUtils.ignoreErrors(Promise.all(promises));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await this.fetchData(true);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            refresher.complete();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										79
									
								
								src/addons/mod/feedback/pages/respondents/respondents.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/addons/mod/feedback/pages/respondents/respondents.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					<ion-header>
 | 
				
			||||||
 | 
					    <ion-toolbar>
 | 
				
			||||||
 | 
					        <ion-buttons slot="start">
 | 
				
			||||||
 | 
					            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					        <ion-title>{{ 'addon.mod_feedback.responses' |translate }}</ion-title>
 | 
				
			||||||
 | 
					    </ion-toolbar>
 | 
				
			||||||
 | 
					</ion-header>
 | 
				
			||||||
 | 
					<ion-content>
 | 
				
			||||||
 | 
					    <core-split-view>
 | 
				
			||||||
 | 
					        <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshFeedback($event.target)">
 | 
				
			||||||
 | 
					            <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
				
			||||||
 | 
					        </ion-refresher>
 | 
				
			||||||
 | 
					        <core-loading [hideUntil]="loaded">
 | 
				
			||||||
 | 
					            <ion-list  class="ion-no-margin">
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-wrap" *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
 | 
				
			||||||
 | 
					                    <ion-label id="addon-feedback-groupslabel">
 | 
				
			||||||
 | 
					                        <ng-container *ngIf="groupInfo.separateGroups">{{'core.groupsseparate' | translate }}</ng-container>
 | 
				
			||||||
 | 
					                        <ng-container *ngIf="groupInfo.visibleGroups">{{'core.groupsvisible' | translate }}</ng-container>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                    <ion-select [(ngModel)]="selectedGroup" (ionChange)="loadAttempts(selectedGroup)"
 | 
				
			||||||
 | 
					                        aria-labelledby="addon-feedback-groupslabel" interface="action-sheet">
 | 
				
			||||||
 | 
					                        <ion-select-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">
 | 
				
			||||||
 | 
					                            {{groupOpt.name}}
 | 
				
			||||||
 | 
					                        </ion-select-option>
 | 
				
			||||||
 | 
					                    </ion-select>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ng-container *ngIf="responses.responses.total > 0">
 | 
				
			||||||
 | 
					                    <ion-item-divider>
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            {{ 'addon.mod_feedback.non_anonymous_entries' | translate : {$a: responses.responses.total } }}
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                    </ion-item-divider>
 | 
				
			||||||
 | 
					                    <ion-item *ngFor="let attempt of responses.responses.attempts" class="ion-text-wrap" tappable detail="true"
 | 
				
			||||||
 | 
					                        (click)="responses.select(attempt)" [class.core-selected-item]="responses.isSelected(attempt)">
 | 
				
			||||||
 | 
					                        <core-user-avatar [user]="attempt" slot="start"></core-user-avatar>
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            <h2>{{ attempt.fullname }}</h2>
 | 
				
			||||||
 | 
					                            <p *ngIf="attempt.timemodified">{{attempt.timemodified * 1000 | coreFormatDate }}</p>
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- Button and spinner to show more attempts. -->
 | 
				
			||||||
 | 
					                    <ion-button *ngIf="responses.responses.canLoadMore && !loadingMore" class="ion-margin" expand="block"
 | 
				
			||||||
 | 
					                        (click)="loadAttempts()">
 | 
				
			||||||
 | 
					                        {{ 'core.loadmore' | translate }}
 | 
				
			||||||
 | 
					                    </ion-button>
 | 
				
			||||||
 | 
					                    <ion-item *ngIf="responses.responses.canLoadMore && loadingMore" class="ion-text-center">
 | 
				
			||||||
 | 
					                        <ion-label><ion-spinner></ion-spinner></ion-label>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ng-container *ngIf="responses.anonResponses.total > 0">
 | 
				
			||||||
 | 
					                    <ion-item-divider>
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            {{ 'addon.mod_feedback.anonymous_entries' |translate : {$a: responses.anonResponses.total } }}
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                    </ion-item-divider>
 | 
				
			||||||
 | 
					                    <ion-item *ngFor="let attempt of responses.anonResponses.attempts" class="ion-text-wrap" tappable detail="true"
 | 
				
			||||||
 | 
					                        (click)="responses.select(attempt)" [class.core-selected-item]="responses.isSelected(attempt)">
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            <h2>{{ 'addon.mod_feedback.response_nr' |translate }}: {{attempt.number}}</h2>
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- Button and spinner to show more attempts. -->
 | 
				
			||||||
 | 
					                    <ion-button *ngIf="responses.anonResponses.canLoadMore && !loadingMore" class="ion-margin" expand="block"
 | 
				
			||||||
 | 
					                        (click)="loadAttempts()">
 | 
				
			||||||
 | 
					                        {{ 'core.loadmore' | translate }}
 | 
				
			||||||
 | 
					                    </ion-button>
 | 
				
			||||||
 | 
					                    <ion-item *ngIf="responses.anonResponses.canLoadMore && loadingMore" class="ion-text-center">
 | 
				
			||||||
 | 
					                        <ion-label><ion-spinner></ion-spinner></ion-label>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					            </ion-list>
 | 
				
			||||||
 | 
					        </core-loading>
 | 
				
			||||||
 | 
					    </core-split-view>
 | 
				
			||||||
 | 
					</ion-content>
 | 
				
			||||||
							
								
								
									
										248
									
								
								src/addons/mod/feedback/pages/respondents/respondents.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								src/addons/mod/feedback/pages/respondents/respondents.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,248 @@
 | 
				
			|||||||
 | 
					// (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 { AfterViewInit, Component, ViewChild } from '@angular/core';
 | 
				
			||||||
 | 
					import { ActivatedRoute } from '@angular/router';
 | 
				
			||||||
 | 
					import { CorePageItemsListManager } from '@classes/page-items-list-manager';
 | 
				
			||||||
 | 
					import { CoreSplitViewComponent } from '@components/split-view/split-view';
 | 
				
			||||||
 | 
					import { IonRefresher } from '@ionic/angular';
 | 
				
			||||||
 | 
					import { CoreGroupInfo, CoreGroups } from '@services/groups';
 | 
				
			||||||
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModFeedback,
 | 
				
			||||||
 | 
					    AddonModFeedbackWSAnonAttempt,
 | 
				
			||||||
 | 
					    AddonModFeedbackWSAttempt,
 | 
				
			||||||
 | 
					    AddonModFeedbackWSFeedback,
 | 
				
			||||||
 | 
					} from '../../services/feedback';
 | 
				
			||||||
 | 
					import { AddonModFeedbackHelper, AddonModFeedbackResponsesAnalysis } from '../../services/feedback-helper';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Page that displays feedback respondents.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'page-addon-mod-feedback-respondents',
 | 
				
			||||||
 | 
					    templateUrl: 'respondents.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModFeedbackRespondentsPage implements AfterViewInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected cmId!: number;
 | 
				
			||||||
 | 
					    protected courseId!: number;
 | 
				
			||||||
 | 
					    protected page = 0;
 | 
				
			||||||
 | 
					    protected feedback?: AddonModFeedbackWSFeedback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    responses: AddonModFeedbackResponsesManager;
 | 
				
			||||||
 | 
					    selectedGroup!: number;
 | 
				
			||||||
 | 
					    groupInfo?: CoreGroupInfo;
 | 
				
			||||||
 | 
					    loaded = false;
 | 
				
			||||||
 | 
					    loadingMore = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        route: ActivatedRoute,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        this.responses = new AddonModFeedbackResponsesManager(
 | 
				
			||||||
 | 
					            route.component,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async ngAfterViewInit(): Promise<void> {
 | 
				
			||||||
 | 
					        this.cmId = CoreNavigator.getRouteNumberParam('cmId')!;
 | 
				
			||||||
 | 
					        this.courseId = CoreNavigator.getRouteNumberParam('courseId')!;
 | 
				
			||||||
 | 
					        this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.fetchData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.responses.start(this.splitView);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch all the data required for the view.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresh Empty events array first.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async fetchData(refresh: boolean = false): Promise<void> {
 | 
				
			||||||
 | 
					        this.page = 0;
 | 
				
			||||||
 | 
					        this.responses.resetItems();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.feedback = await AddonModFeedback.getFeedback(this.courseId, this.cmId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.groupInfo = await CoreGroups.getActivityGroupInfo(this.cmId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.selectedGroup = CoreGroups.validateGroupId(this.selectedGroup, this.groupInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await this.loadGroupAttempts(this.selectedGroup);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!refresh) {
 | 
				
			||||||
 | 
					                // Some call failed on first fetch, go back.
 | 
				
			||||||
 | 
					                CoreNavigator.back();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load Group attempts.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param groupId If defined it will change group if not, it will load more attempts for the same group.
 | 
				
			||||||
 | 
					     * @return Resolved with the attempts loaded.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async loadGroupAttempts(groupId?: number): Promise<void> {
 | 
				
			||||||
 | 
					        if (typeof groupId == 'undefined') {
 | 
				
			||||||
 | 
					            this.page++;
 | 
				
			||||||
 | 
					            this.loadingMore = true;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.selectedGroup = groupId;
 | 
				
			||||||
 | 
					            this.page = 0;
 | 
				
			||||||
 | 
					            this.responses.resetItems();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const responses = await AddonModFeedbackHelper.getResponsesAnalysis(this.feedback!.id, {
 | 
				
			||||||
 | 
					                groupId: this.selectedGroup,
 | 
				
			||||||
 | 
					                page: this.page,
 | 
				
			||||||
 | 
					                cmId: this.cmId,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.responses.setResponses(responses);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.loadingMore = false;
 | 
				
			||||||
 | 
					            this.loaded = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Change selected group or load more attempts.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param groupId Group ID selected. If not defined, it will load more attempts.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async loadAttempts(groupId?: number): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await this.loadGroupAttempts(groupId);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Refresh the attempts.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresher Refresher.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async refreshFeedback(refresher: IonRefresher): Promise<void> {
 | 
				
			||||||
 | 
					        const promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(CoreGroups.invalidateActivityGroupInfo(this.cmId));
 | 
				
			||||||
 | 
					        if (this.feedback) {
 | 
				
			||||||
 | 
					            promises.push(AddonModFeedback.invalidateResponsesAnalysisData(this.feedback.id));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await CoreUtils.ignoreErrors(Promise.all(promises));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await this.fetchData(true);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            refresher.complete();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Type of items that can be held by the entries manager.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					type EntryItem = AddonModFeedbackWSAttempt | AddonModFeedbackWSAnonAttempt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Entries manager.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class AddonModFeedbackResponsesManager extends CorePageItemsListManager<EntryItem> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    responses: AddonModFeedbackResponses = {
 | 
				
			||||||
 | 
					        attempts: [],
 | 
				
			||||||
 | 
					        total: 0,
 | 
				
			||||||
 | 
					        canLoadMore: false,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    anonResponses: AddonModFeedbackAnonResponses = {
 | 
				
			||||||
 | 
					        attempts: [],
 | 
				
			||||||
 | 
					        total: 0,
 | 
				
			||||||
 | 
					        canLoadMore: false,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(pageComponent: unknown) {
 | 
				
			||||||
 | 
					        super(pageComponent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update responses.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param responses Responses.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    setResponses(responses: AddonModFeedbackResponsesAnalysis): void {
 | 
				
			||||||
 | 
					        this.responses.total = responses.totalattempts;
 | 
				
			||||||
 | 
					        this.anonResponses.total = responses.totalanonattempts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.anonResponses.attempts.length < responses.totalanonattempts) {
 | 
				
			||||||
 | 
					            this.anonResponses.attempts = this.anonResponses.attempts.concat(responses.anonattempts);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (this.responses.attempts.length < responses.totalattempts) {
 | 
				
			||||||
 | 
					            this.responses.attempts = this.responses.attempts.concat(responses.attempts);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.anonResponses.canLoadMore = this.anonResponses.attempts.length < responses.totalanonattempts;
 | 
				
			||||||
 | 
					        this.responses.canLoadMore = this.responses.attempts.length < responses.totalattempts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.setItems((<EntryItem[]> this.responses.attempts).concat(this.anonResponses.attempts));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    resetItems(): void {
 | 
				
			||||||
 | 
					        super.resetItems();
 | 
				
			||||||
 | 
					        this.responses.total = 0;
 | 
				
			||||||
 | 
					        this.responses.attempts = [];
 | 
				
			||||||
 | 
					        this.anonResponses.total = 0;
 | 
				
			||||||
 | 
					        this.anonResponses.attempts = [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected getItemPath(entry: EntryItem): string {
 | 
				
			||||||
 | 
					        return `attempt/${entry.id}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AddonModFeedbackResponses = {
 | 
				
			||||||
 | 
					    attempts: AddonModFeedbackWSAttempt[];
 | 
				
			||||||
 | 
					    total: number;
 | 
				
			||||||
 | 
					    canLoadMore: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AddonModFeedbackAnonResponses = {
 | 
				
			||||||
 | 
					    attempts: AddonModFeedbackWSAnonAttempt[];
 | 
				
			||||||
 | 
					    total: number;
 | 
				
			||||||
 | 
					    canLoadMore: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user