// (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 { AddonCompetencyHelper } from '@addons/competency/services/competency-helper'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreCourseModuleSummary } from '@features/course/services/course'; import { CoreUserSummary } from '@features/user/services/user'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { Translate } from '@singletons'; import { AddonCompetencyDataForUserCompetencySummaryWSResponse, AddonCompetencyUserCompetencyPlan, AddonCompetencyUserCompetency, AddonCompetencyUserCompetencyCourse, AddonCompetency, AddonCompetencyDataForPlanPageCompetency, AddonCompetencyDataForCourseCompetenciesPageCompetency, AddonCompetencyProvider, } from '@addons/competency/services/competency'; import { CoreNavigator } from '@services/navigator'; import { ContextLevel } from '@/core/constants'; import { CorePromiseUtils } from '@singletons/promise-utils'; import { ADDON_COMPETENCY_SUMMARY_PAGE } from '@addons/competency/constants'; import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; import { AddonCompetencyPlanCompetenciesSource } from '@addons/competency/classes/competency-plan-competencies-source'; import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; import { AddonCompetencyCourseCompetenciesSource } from '@addons/competency/classes/competency-course-competencies-source'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreUrl } from '@singletons/url'; /** * Page that displays the competency information. */ @Component({ selector: 'page-addon-competency-competency', templateUrl: 'competency.html', }) export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy { competencyLoaded = false; competencies!: AddonCompetencyCompetenciesSwipeManager; planStatus?: number; coursemodules?: CoreCourseModuleSummary[]; user?: CoreUserSummary; competency?: AddonCompetencyDataForUserCompetencySummaryWSResponse; userCompetency?: AddonCompetencyUserCompetencyPlan | AddonCompetencyUserCompetency | AddonCompetencyUserCompetencyCourse; contextLevel?: ContextLevel; contextInstanceId?: number; protected logView: () => void; constructor() { this.logView = CoreTime.once(() => this.performLogView()); try { const planId = CoreNavigator.getRouteNumberParam('planId'); if (!planId) { const courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); const userId = CoreNavigator.getRouteNumberParam('userId'); const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource( AddonCompetencyCourseCompetenciesSource, [courseId, userId], ); this.competencies = new AddonCompetencyCompetenciesSwipeManager(source); return; } const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(AddonCompetencyPlanCompetenciesSource, [planId]); this.competencies = new AddonCompetencyCompetenciesSwipeManager(source); } catch (error) { CoreDomUtils.showErrorModal(error); CoreNavigator.back(); return; } } get competencyFrameworkUrl(): string | undefined { if (!this.competency) { return; } const { pluginbaseurl, framework, pagecontextid } = this.competency.competency.comppath; return `${pluginbaseurl}/competencies.php?competencyframeworkid=${framework.id}&pagecontextid=${pagecontextid}`; } get courseId(): number | undefined { const source = this.competencies.getSource(); if (!(source instanceof AddonCompetencyCourseCompetenciesSource)) { return; } return source.COURSE_ID; } /** * @inheritdoc */ async ngOnInit(): Promise { try { const source = this.competencies.getSource(); await source.reload(); await this.competencies.start(); await this.fetchCompetency(); } finally { this.competencyLoaded = true; } } /** * @inheritdoc */ ngOnDestroy(): void { this.competencies.destroy(); } /** * Fetches the competency and updates the view. * * @returns Promise resolved when done. */ protected async fetchCompetency(): Promise { try { const source = this.competencies.getSource(); this.competency = source instanceof AddonCompetencyPlanCompetenciesSource ? await this.fetchCompetencySummaryFromPlan(source) : await this.fetchCompetencySummaryFromCourse(source); if (this.competency.user.id != CoreSites.getCurrentSiteUserId()) { // Get the user profile from the returned object. this.user = this.competency.user; } this.competency.evidence.forEach((evidence) => { if (evidence.descidentifier) { const key = 'addon.competency.' + evidence.descidentifier; evidence.description = Translate.instant(key, { $a: evidence.desca }); } }); this.logView(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error getting competency data.'); } } /** * Refreshes the competency. * * @param refresher Refresher. */ async refreshCompetency(refresher: HTMLIonRefresherElement): Promise { const source = this.competencies.getSource(); await CorePromiseUtils.ignoreErrors( source instanceof AddonCompetencyPlanCompetenciesSource ? AddonCompetency.invalidateCompetencyInPlan(source.PLAN_ID, this.requireCompetencyId()) : AddonCompetency.invalidateCompetencyInCourse(source.COURSE_ID, this.requireCompetencyId(), source.USER_ID), ); this.fetchCompetency().finally(() => { refresher?.complete(); }); } /** * Opens the summary of a competency. * * @param competencyId Competency Id. */ openCompetencySummary(competencyId: number): void { CoreNavigator.navigate( `../${competencyId}/${ADDON_COMPETENCY_SUMMARY_PAGE}`, { params: { contextLevel: this.contextLevel, contextInstanceId: this.contextInstanceId }, }, ); } /** * Get competency id or fail. * * @returns Competency id. */ private requireCompetencyId(): number { const selectedItem = this.competencies.getSelectedItem(); if (!selectedItem) { throw new Error('Failed to get competency id from selected item'); } return selectedItem.competency.id; } /** * Fetch competency summary from a plan source. * * @param source Plan competencies source. * @returns Competency summary. */ private async fetchCompetencySummaryFromPlan( source: AddonCompetencyPlanCompetenciesSource, ): Promise { const competency = await AddonCompetency.getCompetencyInPlan( source.PLAN_ID, this.requireCompetencyId(), ); this.planStatus = competency.plan.status; if (competency.usercompetencysummary.usercompetency) { competency.usercompetencysummary.usercompetency.statusname = AddonCompetencyHelper.getCompetencyStatusName(competency.usercompetencysummary.usercompetency.status); } this.contextLevel = ContextLevel.USER; this.contextInstanceId = source.user?.id || competency.usercompetencysummary.user.id; this.userCompetency = competency.usercompetencysummary.usercompetencyplan || competency.usercompetencysummary.usercompetency; return competency.usercompetencysummary; } /** * Fetch competency summary from a course source. * * @param source Course competencies source. * @returns Competency summary. */ private async fetchCompetencySummaryFromCourse( source: AddonCompetencyCourseCompetenciesSource, ): Promise { const competency = await AddonCompetency.getCompetencyInCourse( source.COURSE_ID, this.requireCompetencyId(), source.USER_ID, ); this.coursemodules = competency.coursemodules; this.contextLevel = ContextLevel.COURSE; this.contextInstanceId = source.COURSE_ID; this.userCompetency = competency.usercompetencysummary.usercompetencycourse || competency.usercompetencysummary.usercompetency; return competency.usercompetencysummary; } /** * Log view. */ protected async performLogView(): Promise { if (!this.competency) { return; } const source = this.competencies.getSource(); const compId = this.requireCompetencyId(); const name = this.competency.competency.competency.shortname; const userId = source.user?.id; if (source instanceof AddonCompetencyPlanCompetenciesSource) { if (!this.planStatus) { return; } await CorePromiseUtils.ignoreErrors( AddonCompetency.logCompetencyInPlanView(source.PLAN_ID, compId, this.planStatus, name, userId), ); const wsName = this.planStatus === AddonCompetencyProvider.STATUS_COMPLETE ? 'core_competency_user_competency_plan_viewed' : 'core_competency_user_competency_viewed_in_plan'; CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, ws: wsName, name, data: { id: compId, category: 'competency', planid: source.PLAN_ID, planstatus: this.planStatus, userid: userId, }, url: CoreUrl.addParamsToUrl('/admin/tool/lp/user_competency_in_plan.php', { planid: source.PLAN_ID, userid: userId, competencyid: compId, }), }); return; } await CorePromiseUtils.ignoreErrors( AddonCompetency.logCompetencyInCourseView(source.COURSE_ID, compId, name, source.USER_ID), ); CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.VIEW_ITEM, ws: 'core_competency_user_competency_viewed_in_course', name, data: { id: compId, category: 'competency', courseid: source.COURSE_ID, userid: userId, }, url: CoreUrl.addParamsToUrl('/admin/tool/lp/user_competency_in_course.php', { courseid: source.COURSE_ID, competencyid: compId, userid: userId, }), }); } } /** * Helper to manage swiping within a collection of competencies. */ class AddonCompetencyCompetenciesSwipeManager extends CoreSwipeNavigationItemsManager< AddonCompetencyDataForPlanPageCompetency | AddonCompetencyDataForCourseCompetenciesPageCompetency, AddonCompetencyPlanCompetenciesSource | AddonCompetencyCourseCompetenciesSource > { /** * @inheritdoc */ protected getSelectedItemPathFromRoute(route: ActivatedRouteSnapshot | ActivatedRoute): string | null { return CoreNavigator.getRouteParams(route).competencyId; } }