src/app/modules/learn/components/course-consumption/course-player/course-player.component.ts
OnInit
OnDestroy
selector | app-course-player |
styleUrls | course-player.component.scss |
templateUrl | ./course-player.component.html |
constructor(activatedRoute: ActivatedRoute, configService: ConfigService, courseConsumptionService: CourseConsumptionService, windowScrollService: WindowScrollService, router: Router, navigationHelperService: NavigationHelperService, userService: UserService, toasterService: ToasterService, resourceService: ResourceService, popupControlService: PopupControlService, courseBatchService: CourseBatchService, permissionService: PermissionService, externalUrlPreviewService: ExternalUrlPreviewService, coursesService: CoursesService, courseProgressService: CourseProgressService, deviceDetectorService: DeviceDetectorService, telemetryService: TelemetryService, contentUtilsServiceService: ContentUtilsServiceService, layoutService: LayoutService, generaliseLabelService: GeneraliseLabelService, connectionService: ConnectionService, CsCourseService: CsCourseService, notificationService: NotificationServiceImpl)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Parameters :
|
Private _navigateToContent |
_navigateToContent()
|
Returns :
void
|
calculateProgress |
calculateProgress()
|
Returns :
void
|
closeSharePopup | ||||
closeSharePopup(id)
|
||||
Parameters :
Returns :
void
|
collapsedChange |
collapsedChange(event: boolean, index: number)
|
Returns :
void
|
dropdownMenu |
dropdownMenu()
|
Returns :
void
|
Public findContentById | ||||||
findContentById(id: string)
|
||||||
Parameters :
Returns :
any
|
Public forceSync |
forceSync()
|
Returns :
void
|
getAllBatchDetails | ||||
getAllBatchDetails(event)
|
||||
Parameters :
Returns :
void
|
Private getContentState |
getContentState()
|
Returns :
void
|
getDataSetting |
getDataSetting()
|
Returns :
boolean
|
initLayout |
initLayout()
|
Returns :
void
|
isCourseModifiedAfterEnrolment |
isCourseModifiedAfterEnrolment()
|
Returns :
void
|
isEnrollmentAllowed | ||||
isEnrollmentAllowed(enrollmentEndDate)
|
||||
Parameters :
Returns :
any
|
isExpanded | ||||||
isExpanded(index: number)
|
||||||
Parameters :
Returns :
boolean
|
logTelemetry | |||||||||
logTelemetry(id, content?: literal type)
|
|||||||||
Parameters :
Returns :
void
|
navigateToConfigureCertificate |
navigateToConfigureCertificate(mode: string, batchId: string)
|
Returns :
void
|
Public navigateToContent |
navigateToContent(event: any, collectionUnit?: any, id?)
|
Returns :
void
|
navigateToPlayerPage | |||||||||
navigateToPlayerPage(collectionUnit: any, event?)
|
|||||||||
Parameters :
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onCourseCompleteClose |
onCourseCompleteClose()
|
Returns :
void
|
onPopupClose | ||||
onPopupClose(event)
|
||||
Parameters :
Returns :
void
|
Private parseChildContent |
parseChildContent()
|
Returns :
void
|
Private setTelemetryCourseImpression |
setTelemetryCourseImpression()
|
Returns :
void
|
setTelemetryShareData | ||||
setTelemetryShareData(param)
|
||||
Parameters :
Returns :
void
|
Private setTelemetryStartEndData |
setTelemetryStartEndData()
|
Returns :
void
|
shareUnitLink | ||||||
shareUnitLink(unit: any)
|
||||||
Parameters :
Returns :
void
|
validateBatchDate | ||||
validateBatchDate(batch)
|
||||
Parameters :
Returns :
any
|
_routerStateContentStatus |
Type : any
|
Public activatedRoute |
Type : ActivatedRoute
|
addToGroup |
Default value : false
|
assessmentMaxAttempts |
Type : number
|
Public batchId |
Type : string
|
Public batchMessage |
Type : any
|
cardType |
Type : TocCardType
|
Default value : TocCardType.COURSE
|
certificateDescription |
Type : object
|
Default value : {}
|
Public closeContentIntractEdata |
Type : IInteractEventEdata
|
Public collectionTreeOptions |
Type : ICollectionTreeOptions
|
Public consentConfig |
Type : any
|
Public contentDetails |
Type : literal type[]
|
Default value : []
|
Public contentId |
Type : string
|
Public contentIds |
Type : []
|
Default value : []
|
contentInteract |
Type : IInteractEventEdata
|
Public contentInteractObject |
Type : IInteractEventObject
|
Public contentStatus |
Type : []
|
Default value : []
|
Public contentTitle |
Type : string
|
continueInteract |
Type : IInteractEventEdata
|
Public courseBatchService |
Type : CourseBatchService
|
Public courseConsent |
Type : string
|
Default value : 'course-consent'
|
Public courseHierarchy |
Type : any
|
Private courseId |
Type : string
|
Public courseInteractObject |
Type : IInteractEventObject
|
courseMentor |
Default value : false
|
Public courseProgressData |
Type : any
|
Public coursesService |
Type : CoursesService
|
Public courseStatus |
Type : string
|
createdBatchId |
Type : string
|
dropdownContent |
Default value : true
|
Public enrolledBatchInfo |
Type : any
|
Public enrolledCourse |
Default value : false
|
Public externalUrlPreviewService |
Type : ExternalUrlPreviewService
|
Public flaggedCourse |
Default value : false
|
Public generaliseLabelService |
Type : GeneraliseLabelService
|
groupId |
hasPreviewPermission |
Default value : false
|
isConnected |
Default value : false
|
isEnrolledCourseUpdated |
Default value : false
|
isExpandedAll |
Type : boolean
|
isFirst |
Default value : false
|
isModuleExpanded |
Default value : false
|
Public istrustedClickXurl |
Default value : false
|
layoutConfiguration |
Public layoutService |
Type : LayoutService
|
Public loader |
Default value : true
|
modal |
Decorators :
@ViewChild('modal')
|
navigateToContentObject |
Type : any
|
Public navigationHelperService |
Type : NavigationHelperService
|
pageId |
Type : string
|
Public permissionService |
Type : PermissionService
|
Public playerConfig |
Type : any
|
Public popupControlService |
Type : PopupControlService
|
popupMode |
Type : string
|
Public previewContentRoles |
Type : []
|
Default value : ['COURSE_MENTOR', 'CONTENT_REVIEWER', 'CONTENT_CREATOR', 'CONTENT_CREATION']
|
progress |
Type : number
|
Default value : 0
|
progressToDisplay |
Type : number
|
Default value : 0
|
Public router |
Type : Router
|
shareLink |
Type : string
|
shareLinkModal |
Default value : false
|
showConfirmationPopup |
Default value : false
|
showCourseCompleteMessage |
Default value : false
|
showDataSettingSection |
Default value : false
|
Public showExtContentMsg |
Default value : false
|
showForceSync |
Default value : true
|
showJoinModal |
Default value : false
|
Public showJoinTrainingModal |
Default value : false
|
showLastAttemptsModal |
Default value : false
|
startInteract |
Type : IInteractEventEdata
|
telemetryCdata |
Type : Array<literal type>
|
Default value : []
|
Public telemetryContentImpression |
Type : IImpressionEventInput
|
Public telemetryCourseEndEvent |
Type : IEndEventInput
|
Public telemetryCourseImpression |
Type : IImpressionEventInput
|
Public telemetryCourseStart |
Type : IStartEventInput
|
Public telemetryService |
Type : TelemetryService
|
telemetryShareData |
Type : Array<ITelemetryShare>
|
tocId |
Public todayDate |
Default value : dayjs(new Date()).format('YYYY-MM-DD')
|
Public treeModel |
Type : any
|
Public unsubscribe |
Default value : new Subject<void>()
|
Public windowScrollService |
Type : WindowScrollService
|
import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
import { TocCardType } from '@project-sunbird/common-consumption'
import { CoursesService, PermissionService, UserService, GeneraliseLabelService } from '@sunbird/core';
import {
ConfigService, ExternalUrlPreviewService, ICollectionTreeOptions, NavigationHelperService,
ResourceService, ToasterService, WindowScrollService, ITelemetryShare, LayoutService
} from '@sunbird/shared';
import { IEndEventInput, IImpressionEventInput, IInteractEventEdata, IInteractEventObject, IStartEventInput, TelemetryService } from '@sunbird/telemetry';
import * as _ from 'lodash-es';
import { DeviceDetectorService } from 'ngx-device-detector';
import { combineLatest, merge, Subject } from 'rxjs';
import { map, mergeMap, takeUntil } from 'rxjs/operators';
import TreeModel from 'tree-model';
import { PopupControlService } from '../../../../../service/popup-control.service';
import { CourseBatchService, CourseConsumptionService, CourseProgressService } from './../../../services';
import { ContentUtilsServiceService, ConnectionService } from '@sunbird/shared';
import dayjs from 'dayjs';
import { NotificationServiceImpl } from '../../../../notification/services/notification/notification-service-impl';
import { CsCourseService } from '@project-sunbird/client-services/services/course/interface';
@Component({
selector: 'app-course-player',
templateUrl: './course-player.component.html',
styleUrls: ['course-player.component.scss']
})
export class CoursePlayerComponent implements OnInit, OnDestroy {
@ViewChild('modal') modal;
public courseInteractObject: IInteractEventObject;
public contentInteractObject: IInteractEventObject;
public closeContentIntractEdata: IInteractEventEdata;
private courseId: string;
public batchId: string;
public enrolledCourse = false;
public contentId: string;
public courseStatus: string;
public flaggedCourse = false;
public contentTitle: string;
public playerConfig: any;
public loader = true;
public courseConsent = 'course-consent';
public courseHierarchy: any;
public istrustedClickXurl = false;
public telemetryCourseImpression: IImpressionEventInput;
public telemetryContentImpression: IImpressionEventInput;
public telemetryCourseEndEvent: IEndEventInput;
public telemetryCourseStart: IStartEventInput;
public contentIds = [];
public courseProgressData: any;
public contentStatus = [];
public contentDetails: { title: string, id: string, parentId: string }[] = [];
public enrolledBatchInfo: any;
public treeModel: any;
public consentConfig: any;
public showExtContentMsg = false;
public previewContentRoles = ['COURSE_MENTOR', 'CONTENT_REVIEWER', 'CONTENT_CREATOR', 'CONTENT_CREATION'];
public collectionTreeOptions: ICollectionTreeOptions;
public unsubscribe = new Subject<void>();
public showJoinTrainingModal = false;
telemetryCdata: Array<{}> = [];
pageId: string;
cardType: TocCardType = TocCardType.COURSE;
hasPreviewPermission = false;
contentInteract: IInteractEventEdata;
startInteract: IInteractEventEdata;
continueInteract: IInteractEventEdata;
shareLinkModal = false;
telemetryShareData: Array<ITelemetryShare>;
shareLink: string;
progress = 0;
isExpandedAll: boolean;
isFirst = false;
addToGroup = false;
isModuleExpanded = false;
isEnrolledCourseUpdated = false;
layoutConfiguration;
certificateDescription = {};
showCourseCompleteMessage = false;
showConfirmationPopup = false;
popupMode: string;
createdBatchId: string;
courseMentor = false;
progressToDisplay = 0;
public todayDate = dayjs(new Date()).format('YYYY-MM-DD');
public batchMessage: any;
showDataSettingSection = false;
assessmentMaxAttempts: number;
showJoinModal = false;
tocId;
groupId;
showLastAttemptsModal = false;
navigateToContentObject: any;
_routerStateContentStatus: any;
isConnected = false;
dropdownContent = true;
showForceSync = true;
constructor(
public activatedRoute: ActivatedRoute,
private configService: ConfigService,
private courseConsumptionService: CourseConsumptionService,
public windowScrollService: WindowScrollService,
public router: Router,
public navigationHelperService: NavigationHelperService,
private userService: UserService,
private toasterService: ToasterService,
private resourceService: ResourceService,
public popupControlService: PopupControlService,
public courseBatchService: CourseBatchService,
public permissionService: PermissionService,
public externalUrlPreviewService: ExternalUrlPreviewService,
public coursesService: CoursesService,
private courseProgressService: CourseProgressService,
private deviceDetectorService: DeviceDetectorService,
public telemetryService: TelemetryService,
private contentUtilsServiceService: ContentUtilsServiceService,
public layoutService: LayoutService,
public generaliseLabelService: GeneraliseLabelService,
private connectionService: ConnectionService,
@Inject('CS_COURSE_SERVICE') private CsCourseService: CsCourseService,
@Inject('SB_NOTIFICATION_SERVICE') private notificationService: NotificationServiceImpl
) {
this.router.onSameUrlNavigation = 'ignore';
this.collectionTreeOptions = this.configService.appConfig.collectionTreeOptions;
// this.assessmentMaxAttempts = this.configService.appConfig.CourseConsumption.selfAssessMaxLimit;
}
ngOnInit() {
if (this.permissionService.checkRolesPermissions(['COURSE_MENTOR'])) {
this.courseMentor = true;
} else {
this.courseMentor = false;
}
this.connectionService.monitor()
.pipe(takeUntil(this.unsubscribe)).subscribe(isConnected => {
this.isConnected = isConnected;
});
// Set consetnt pop up configuration here
this.consentConfig = {
tncLink: _.get(this.resourceService, 'frmelmnts.lbl.tncLabelLink'),
tncText: _.get(this.resourceService, 'frmelmnts.lbl.agreeToShareDetails')
};
this.initLayout();
this.courseProgressService.courseProgressData.pipe(
takeUntil(this.unsubscribe))
.subscribe(courseProgressData => {
this.courseProgressData = courseProgressData;
this.progress = courseProgressData.progress ? Math.floor(courseProgressData.progress) : 0;
if (this.activatedRoute.snapshot.queryParams.showCourseCompleteMessage === 'true') {
this.showCourseCompleteMessage = this.progress >= 100 ? true : false;
if (this.showCourseCompleteMessage) {
this.notificationService.fetchNotificationList();
}
const queryParams = this.tocId ? { textbook: this.tocId } : {};
this.router.navigate(['.'], { relativeTo: this.activatedRoute, queryParams, replaceUrl: true });
}
});
this.courseConsumptionService.updateContentConsumedStatus
.pipe(takeUntil(this.unsubscribe))
.subscribe((data) => {
this.courseHierarchy = _.cloneDeep(data.courseHierarchy);
this.batchId = data.batchId;
this.courseId = data.courseId;
this.contentIds = this.courseConsumptionService.parseChildren(this.courseHierarchy);
this.getContentState();
});
this.courseConsumptionService.launchPlayer
.pipe(takeUntil(this.unsubscribe))
.subscribe(data => {
/* istanbul ignore else */
if (_.get(this.courseHierarchy, 'children.length')) {
const unconsumedUnit = this.courseHierarchy.children.find(item => !item.isUnitConsumed);
const unit = unconsumedUnit ? unconsumedUnit : this.courseHierarchy;
this.navigateToPlayerPage(unit);
}
});
this.activatedRoute.queryParams
.pipe(takeUntil(this.unsubscribe))
.subscribe(response => {
this.addToGroup = Boolean(response.groupId);
this.groupId = _.get(response, 'groupId');
this.tocId = response.textbook || undefined;
});
this.courseConsumptionService.updateContentState
.pipe(takeUntil(this.unsubscribe))
.subscribe(data => {
this.getContentState();
});
this.pageId = this.activatedRoute.snapshot.data.telemetry.pageid;
merge(this.activatedRoute.params.pipe(
mergeMap(({ courseId, batchId, courseStatus }) => {
this.courseId = courseId;
this.batchId = batchId;
this.courseStatus = courseStatus;
if (this.batchId) {
this.telemetryCdata = [{ id: this.batchId, type: 'CourseBatch' }];
}
this.setTelemetryCourseImpression();
const inputParams = { params: this.configService.appConfig.CourseConsumption.contentApiQueryParams };
/* istanbul ignore else */
if (this.batchId) {
return combineLatest([
this.courseConsumptionService.getCourseHierarchy(courseId, inputParams),
this.courseBatchService.getEnrolledBatchDetails(this.batchId)
]).pipe(map(results => ({ courseHierarchy: results[0], enrolledBatchDetails: results[1] })));
}
return this.courseConsumptionService.getCourseHierarchy(courseId, inputParams)
.pipe(map(courseHierarchy => ({ courseHierarchy })));
})))
.pipe(takeUntil(this.unsubscribe))
.subscribe(({ courseHierarchy, enrolledBatchDetails }: any) => {
this.courseHierarchy = courseHierarchy;
this.layoutService.updateSelectedContentType.emit(this.courseHierarchy.contentType);
this.isExpandedAll = this.courseHierarchy.children.length === 1 ? true : false;
this.courseInteractObject = {
id: this.courseHierarchy.identifier,
type: 'Course',
ver: this.courseHierarchy.pkgVersion ? this.courseHierarchy.pkgVersion.toString() : '1.0'
};
/* istanbul ignore else */
if (this.courseHierarchy.status === 'Flagged') {
this.flaggedCourse = true;
}
this.parseChildContent();
if (this.batchId) {
this.enrolledBatchInfo = enrolledBatchDetails;
this.certificateDescription = this.courseBatchService.getcertificateDescription(this.enrolledBatchInfo);
this.enrolledCourse = true;
setTimeout(() => {
this.setTelemetryStartEndData();
}, 100);
/* istanbul ignore else */
if (_.hasIn(this.enrolledBatchInfo, 'status') && this.contentIds.length) {
this.getContentState();
}
this.isCourseModifiedAfterEnrolment();
} else if (this.courseStatus === 'Unlisted' || this.permissionService.checkRolesPermissions(this.previewContentRoles)
|| this.courseHierarchy.createdBy === this.userService.userid) {
this.hasPreviewPermission = true;
}
this.showDataSettingSection = this.getDataSetting();
this.loader = false;
}, (error) => {
this.loader = false;
this.toasterService.error(this.resourceService.messages.emsg.m0005); // need to change message
});
this.courseBatchService.updateEvent.subscribe((event) => {
setTimeout(() => {
if (_.get(event, 'event') === 'issueCert' && _.get(event, 'value') === 'yes') {
this.createdBatchId = _.get(event, 'batchId');
if (!_.get(event, 'isCertInBatch')) {
this.showConfirmationPopup = true;
this.popupMode = _.get(event, 'mode');
}
}
}, 1000);
});
const isForceSynced = localStorage.getItem(this.courseId + '_isforce-sync');
if (isForceSynced) {
this.showForceSync = false;
}
}
/**
* @since - release-3.2.10
* @param {object} event
* @description - it will navigate to add-certificate page or will trigger
* telemetry event based on the event mode.
*/
onPopupClose(event) {
if (_.get(event, 'mode') === 'add-certificates') {
this.navigateToConfigureCertificate('add', _.get(event, 'batchId'));
this.logTelemetry('choose-to-add-certificate');
} else {
this.logTelemetry('deny-add-certificate');
}
this.showConfirmationPopup = false;
}
/**
* @since - release-3.2.10
* @param {string} mode
* @param {string} batchId
* @description - It will navigate to certificate-configuration page.
*/
navigateToConfigureCertificate(mode: string, batchId: string) {
this.router.navigate([`/certs/configure/certificate`], {
queryParams: {
type: mode,
courseId: this.courseId,
batchId: batchId
}
});
}
initLayout() {
this.layoutConfiguration = this.layoutService.initlayoutConfig();
this.layoutService.switchableLayout().
pipe(takeUntil(this.unsubscribe)).subscribe(layoutConfig => {
if (layoutConfig != null) {
this.layoutConfiguration = layoutConfig.layout;
}
});
}
private parseChildContent() {
this.contentIds = [];
const model = new TreeModel();
const mimeTypeCount = {};
this.treeModel = model.parse(this.courseHierarchy);
this.treeModel.walk((node) => {
if (node.model.mimeType !== 'application/vnd.ekstep.content-collection') {
if (mimeTypeCount[node.model.mimeType]) {
mimeTypeCount[node.model.mimeType] += 1;
} else {
mimeTypeCount[node.model.mimeType] = 1;
}
this.contentIds.push(node.model.identifier);
}
});
}
private getContentState() {
const fieldsArray: Array<string> = ['progress', 'score'];
const req: any = {
userId: this.userService.userid,
courseId: this.courseId,
contentIds: this.contentIds,
batchId: this.batchId,
fields: fieldsArray
};
this.CsCourseService
.getContentState(req, { apiPath: '/content/course/v1' })
.pipe(takeUntil(this.unsubscribe))
.subscribe((res) => {
const _parsedResponse = this.courseProgressService.getContentProgressState(req, res);
this.progressToDisplay = Math.floor((_parsedResponse.completedCount / this.courseHierarchy.leafNodesCount) * 100);
this.contentStatus = _parsedResponse.content || [];
this._routerStateContentStatus = _parsedResponse;
this.calculateProgress();
}, error => {
console.log('Content state read CSL API failed ', error);
});
}
public findContentById(id: string) {
return this.treeModel.first(node => node.model.identifier === id);
}
public navigateToContent(event: any, collectionUnit?: any, id?): void {
this.navigateToContentObject = {
event: event,
collectionUnit: collectionUnit,
id: id
};
if (_.get(event, 'event.isDisabled')) {
return this.toasterService.error(_.get(this.resourceService, 'frmelmnts.lbl.selfAssessMaxAttempt'));
} else if (_.get(event, 'event.isLastAttempt')) {
this.showLastAttemptsModal = true;
} else {
this._navigateToContent();
}
}
private _navigateToContent() {
this.showLastAttemptsModal = false;
/* istanbul ignore else */
if (!this.addToGroup) {
this.logTelemetry(this.navigateToContentObject.id, this.navigateToContentObject.event.data);
} else {
this.logTelemetry('play-content-group', this.navigateToContentObject.event.data);
}
/* istanbul ignore else */
setTimeout(() => {
if (!this.showLastAttemptsModal && !_.isEmpty(this.navigateToContentObject.event.event)) {
this.navigateToPlayerPage(this.navigateToContentObject.collectionUnit, this.navigateToContentObject.event);
}
}, 100);
}
private setTelemetryStartEndData() {
this.telemetryCdata = [{ 'type': 'Course', 'id': this.courseId }];
if (this.batchId) {
this.telemetryCdata.push({ id: this.batchId, type: 'CourseBatch' });
}
if (this.groupId && !_.find(this.telemetryCdata, {id: this.groupId})) {
this.telemetryCdata.push({
id: this.groupId,
type: 'Group'
});
}
const deviceInfo = this.deviceDetectorService.getDeviceInfo();
this.telemetryCourseStart = {
context: {
env: this.activatedRoute.snapshot.data.telemetry.env,
cdata: this.telemetryCdata
},
object: {
id: this.courseId,
type: this.activatedRoute.snapshot.data.telemetry.object.type,
ver: this.activatedRoute.snapshot.data.telemetry.object.ver,
rollup: {
l1: this.courseId
}
},
edata: {
type: this.activatedRoute.snapshot.data.telemetry.type,
pageid: this.activatedRoute.snapshot.data.telemetry.pageid,
mode: 'play',
uaspec: {
agent: deviceInfo.browser,
ver: deviceInfo.browser_version,
system: deviceInfo.os_version,
platform: deviceInfo.os,
raw: deviceInfo.userAgent
}
}
};
this.telemetryCourseEndEvent = {
object: {
id: this.courseId,
type: this.activatedRoute.snapshot.data.telemetry.object.type,
ver: this.activatedRoute.snapshot.data.telemetry.object.ver,
rollup: {
l1: this.courseId
}
},
context: {
env: this.activatedRoute.snapshot.data.telemetry.env,
cdata: this.telemetryCdata
},
edata: {
type: this.activatedRoute.snapshot.data.telemetry.type,
pageid: this.activatedRoute.snapshot.data.telemetry.pageid,
mode: 'play'
}
};
}
private setTelemetryCourseImpression() {
if (this.groupId && !_.find(this.telemetryCdata, {id: this.groupId})) {
this.telemetryCdata.push({
id: this.groupId,
type: 'Group'
});
}
this.telemetryCourseImpression = {
context: {
env: this.activatedRoute.snapshot.data.telemetry.env,
cdata: this.telemetryCdata
},
edata: {
type: this.activatedRoute.snapshot.data.telemetry.type,
pageid: this.activatedRoute.snapshot.data.telemetry.pageid,
uri: this.router.url,
},
object: {
id: this.courseId,
type: 'Course',
ver: '1.0',
rollup: {
l1: this.courseId
}
}
};
}
isExpanded(index: number) {
if (_.isUndefined(this.isExpandedAll) && !(this.isModuleExpanded) && index === 0) {
return true;
}
return this.isExpandedAll;
}
collapsedChange(event: boolean, index: number) {
if (event === false) {
_.map(_.get(this.courseHierarchy, 'children'), (unit, key) => {
unit.collapsed = key === index ? false : true;
});
}
}
navigateToPlayerPage(collectionUnit: any, event?) {
if ((this.enrolledCourse && this.batchId) || this.hasPreviewPermission) {
const navigationExtras: NavigationExtras = {
queryParams: { batchId: this.batchId, courseId: this.courseId, courseName: this.courseHierarchy.name },
state: { contentStatus: this._routerStateContentStatus }
};
if (this.tocId) {
navigationExtras.queryParams['textbook'] = this.tocId;
}
if (this.groupId) {
navigationExtras.queryParams['groupId'] = this.groupId;
}
if (event && !_.isEmpty(event.event)) {
navigationExtras.queryParams.selectedContent = event.data.identifier;
} else if (collectionUnit.mimeType === 'application/vnd.ekstep.content-collection' && _.get(collectionUnit, 'children.length')
&& _.get(this.contentStatus, 'length')) {
const parsedChildren = this.courseConsumptionService.parseChildren(collectionUnit);
const collectionChildren = [];
this.contentStatus.forEach(item => {
if (parsedChildren.find(content => content === item.contentId)) {
collectionChildren.push(item);
}
});
/* istanbul ignore else */
if (collectionChildren.length) {
const selectedContent: any = collectionChildren.find(item => item.status !== 2);
/* istanbul ignore else */
if (selectedContent) {
navigationExtras.queryParams.selectedContent = selectedContent.contentId;
}
}
}
this.router.navigate(['/learn/course/play', collectionUnit.identifier], navigationExtras);
} else {
this.batchMessage = _.get(this.generaliseLabelService, 'frmelmnts.lbl.joinTrainingToAcessContent');
this.showJoinTrainingModal = true;
if (this.courseHierarchy.batches && this.courseHierarchy.batches.length === 1) {
this.batchMessage = this.validateBatchDate(this.courseHierarchy.batches);
} else if (this.courseHierarchy.batches && this.courseHierarchy.batches.length === 2) {
const allBatchList = _.filter(_.get(this.courseHierarchy, 'batches'), (batch) => {
return !this.isEnrollmentAllowed(_.get(batch, 'enrollmentEndDate'));
});
this.batchMessage = this.validateBatchDate(allBatchList);
}
}
}
validateBatchDate(batch) {
let batchMessage = this.generaliseLabelService.frmelmnts.lbl.joinTrainingToAcessContent;
if (batch && batch.length === 1) {
const currentDate = new Date();
const batchStartDate = new Date(batch[0].startDate);
const batchenrollEndDate = batch[0].enrollmentEndDate ? new Date(batch[0].enrollmentEndDate) : null;
if (batchStartDate > currentDate) {
batchMessage = (this.resourceService.messages.emsg.m009).replace('{startDate}', batch[0].startDate);
} else if (batchenrollEndDate !== null && batchenrollEndDate < currentDate) {
batchMessage = (this.resourceService.messages.emsg.m008).replace('{endDate}', batch[0].enrollmentEndDate);
}
}
return batchMessage;
}
isEnrollmentAllowed(enrollmentEndDate) {
return dayjs(enrollmentEndDate).isBefore(this.todayDate);
}
calculateProgress() {
/* istanbul ignore else */
if (_.get(this.courseHierarchy, 'children')) {
this.courseHierarchy.children.forEach(unit => {
if (unit.mimeType === 'application/vnd.ekstep.content-collection') {
let consumedContents = [];
let flattenDeepContents = [];
/* istanbul ignore else */
if (_.get(unit, 'children.length')) {
flattenDeepContents = this.courseConsumptionService.flattenDeep(unit.children).filter(item => item.mimeType !== 'application/vnd.ekstep.content-collection' && item.mimeType !== 'application/vnd.sunbird.question');
/* istanbul ignore else */
if (this.contentStatus.length) {
consumedContents = flattenDeepContents.filter(o => {
return this.contentStatus.some(({ contentId, status }) => o.identifier === contentId && status === 2);
});
}
}
unit.consumedContent = consumedContents.length;
unit.contentCount = flattenDeepContents.length;
unit.isUnitConsumed = consumedContents.length === flattenDeepContents.length;
unit.isUnitConsumptionStart = false;
if (consumedContents.length) {
unit.progress = Math.round((consumedContents.length / flattenDeepContents.length) * 100);
unit.isUnitConsumptionStart = true;
} else {
unit.progress = 0;
unit.isUnitConsumptionStart = false;
}
} else {
const consumedContent = this.contentStatus.filter(({ contentId, status }) => unit.identifier === contentId && status === 2);
unit.consumedContent = consumedContent.length;
unit.contentCount = 1;
unit.isUnitConsumed = consumedContent.length === 1;
unit.progress = consumedContent.length ? 100 : 0;
unit.isUnitConsumptionStart = Boolean(consumedContent.length);
}
});
}
}
logTelemetry(id, content?: {}) {
if (this.batchId) {
this.telemetryCdata = [{ id: this.batchId, type: 'CourseBatch' }];
}
const objectRollUp = this.courseConsumptionService.getContentRollUp(this.courseHierarchy, _.get(content, 'identifier'));
const interactData = {
context: {
env: _.get(this.activatedRoute.snapshot.data.telemetry, 'env') || 'content',
cdata: this.telemetryCdata || []
},
edata: {
id: id,
type: 'CLICK',
pageid: _.get(this.activatedRoute.snapshot.data.telemetry, 'pageid') || 'course-details',
},
object: {
id: content ? _.get(content, 'identifier') : this.activatedRoute.snapshot.params.courseId,
type: content ? _.get(content, 'contentType') : 'Course',
ver: content ? `${_.get(content, 'pkgVersion')}` : `1.0`,
rollup: this.courseConsumptionService.getRollUp(objectRollUp) || {}
}
};
if (this.groupId && !_.find(this.telemetryCdata, {id: this.groupId})) {
interactData.context.cdata.push({
id: this.groupId,
type: 'Group'
});
}
this.telemetryService.interact(interactData);
}
getAllBatchDetails(event) {
this.courseConsumptionService.getAllOpenBatches(event);
}
shareUnitLink(unit: any) {
this.shareLink = `${this.contentUtilsServiceService.getCoursePublicShareUrl(this.courseId)}?moduleId=${unit.identifier}`;
this.shareLinkModal = true;
this.setTelemetryShareData(this.courseHierarchy);
}
setTelemetryShareData(param) {
this.telemetryShareData = [{
id: param.identifier,
type: param.contentType,
ver: param.pkgVersion ? param.pkgVersion.toString() : '1.0'
}];
}
closeSharePopup(id) {
this.shareLinkModal = false;
const interactData = {
context: {
env: _.get(this.activatedRoute.snapshot.data.telemetry, 'env') || 'content',
cdata: this.telemetryCdata
},
edata: {
id: id,
type: 'click',
pageid: _.get(this.activatedRoute.snapshot.data.telemetry, 'pageid') || 'course-details',
},
object: {
id: _.get(this.courseHierarchy, 'identifier'),
type: _.get(this.courseHierarchy, 'contentType') || 'Course',
ver: `${_.get(this.courseHierarchy, 'pkgVersion')}` || `1.0`,
rollup: { l1: this.courseId }
}
};
if (this.groupId && !_.find(this.telemetryCdata, {id: this.groupId})) {
interactData.context.cdata.push({
id: this.groupId,
type: 'Group'
});
}
this.telemetryService.interact(interactData);
}
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
isCourseModifiedAfterEnrolment() {
this.coursesService.getEnrolledCourses().pipe(
takeUntil(this.unsubscribe))
.subscribe((data) => {
const enrolledCourse = _.find(_.get(data, 'result.courses'), (course) => course.courseId === this.courseId);
const enrolledCourseDateTime = new Date(enrolledCourse.enrolledDate).getTime();
const courseLastUpdatedOn = new Date(this.courseHierarchy.lastUpdatedOn).getTime();
this.isEnrolledCourseUpdated = (enrolledCourse && (enrolledCourseDateTime < courseLastUpdatedOn)) || false;
});
}
onCourseCompleteClose() {
this.showCourseCompleteMessage = false;
}
getDataSetting() {
const userId = _.get(this.userService, 'userid');
const isConsentGiven = _.upperCase(_.get(this.courseHierarchy, 'userConsent')) === 'YES';
const isMinor = _.get(this.userService, 'userProfile')?.isMinor;
const isManagedUser = _.get(this.userService, 'userProfile').managedBy;
const canViewDashboard = this.courseConsumptionService.canViewDashboard(this.courseHierarchy);
return (userId && isConsentGiven && (!isMinor || isManagedUser) && !canViewDashboard && this.enrolledCourse);
}
dropdownMenu() {
this.dropdownContent = !this.dropdownContent;
}
public forceSync() {
localStorage.setItem(this.courseId + '_isforce-sync', 'true');
this.showForceSync = false;
this.closeSharePopup('force-sync');
this.dropdownContent = !this.dropdownContent;
const req = {
'courseId': this.courseId,
'batchId': this.batchId,
'userId': _.get(this.userService, 'userid')
};
this.CsCourseService.updateContentState(req, { apiPath: '/content/course/v1' })
.pipe(takeUntil(this.unsubscribe))
.subscribe((res) => {
this.toasterService.success(this.resourceService.frmelmnts.lbl.forceSyncsuccess);
}, error => {
console.log('Content state update CSL API failed ', error);
});
}
}
<!-- Accordion and batch card section start -->
<div *ngIf="!loader" [appTelemetryStart]="telemetryCourseStart" [appTelemetryEnd]="telemetryCourseEndEvent">
<div class="ui container relative9" [appTelemetryImpression]="telemetryCourseImpression">
<div class="sb-course-details sb-g sb-g--gap24 py-16 text-left cc-player">
<div class="sb-course-details__training sb-g-col-xs-12 sb-g-col-md-9 sb-g-col-lg-9 sb-g-col-xxxl-12">
{{ generaliseLabelService?.frmelmnts?.lbl?.LastUpdatedOn }}
<!-- Course details for mobile view -->
<!-- <div class="sb-bg-color-white p-8 mb-16 mobile only"
*ngIf="enrolledBatchInfo?.cert_templates; else noMobileCertificate">
<div class="certified-course__certificate d-inline-flex flex-ai-center py-8 px-16 width-100">
<img src="assets/images/certificate-icon.png" alt="Certificate">
<span
class="fnormal sb-color-primary ml-16 font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseContainCertificate}}</span>
</div>
</div>
<ng-template #noMobileCertificate>
<div class="sb-bg-color-white p-8 mb-16 mobile only">
<div class="certified-course__certificate d-inline-flex flex-ai-center py-8 px-16 width-100">
<img src="assets/images/certificate-icon.png" alt="Certificate">
<span
class="fnormal sb-color-primary ml-16 font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseDontContainCertificate}}</span>
</div>
</div>
</ng-template> -->
<div *ngIf="isEnrolledCourseUpdated" class="mobile only">
<div class="sb-no-course-found my-16 d-flex flex-ai-center">
<div><img src="assets/images/alert.svg" alt="alert-image" width="20px" height="20px"></div>
<div class="ml-8">{{ generaliseLabelService?.frmelmnts?.lbl?.courseLastUpdatedOn }} {{
courseHierarchy?.lastUpdatedOn | date:'dd/MM/yyyy' }}</div>
</div>
</div>
<div class="sb-bg-color-white p-8 mb-16 mobile only sbt-certified-course"
*ngIf="enrolledCourse && !enrolledBatchInfo?.cert_templates">
<div class="certified-course__certificate d-inline-flex flex-ai-center py-8 px-16 width-100">
<img src="assets/images/certificate-icon.png" alt="Certificate">
<span
class="fnormal sb-color-primary ml-16 font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseDontContainCertificate}}</span>
</div>
</div>
<!-- Course details displayed on top when course is not enrolled -->
<app-course-details [courseHierarchy]="courseHierarchy" *ngIf="!enrolledCourse"></app-course-details>
<div class="training-modules my-16">
<div class="training-modules__title d-flex flex-ai-center mb-16">
<h4 class="sb-pageSection-title sb-pageSection-title-light">
{{resourceService?.frmelmnts?.lbl?.coursestructure}}</h4>
<button *ngIf="!isExpandedAll"
class="sb-btn sb-btn-normal sb-btn-outline-primary ml-auto d-flex flex-ai-center" tabindex="0"
(click)="isExpandedAll = true; logTelemetry('expand-all', courseHierarchy);"><img
src="assets/images/plus.png" class="mr-8" alt=""
width="12px">{{resourceService?.frmelmnts?.lbl?.expandAll}}</button>
<button *ngIf="isExpandedAll"
class="sb-btn sb-btn-normal sb-btn-outline-primary ml-auto d-flex flex-ai-center" tabindex="0"
(click)="isExpandedAll = false; logTelemetry('collapse-all', courseHierarchy);"><img
src="assets/images/collapse.png" class="mr-8" alt=""
width="12px">{{resourceService?.frmelmnts?.lbl?.collapseAll}}</button>
</div>
<div *ngFor="let item of courseHierarchy?.children; let index = index">
<sb-accordion [multi]="false">
<sb-accordion-item [expanded]="isExpanded(index)" (collapsedChange)="collapsedChange($event, index)"
[collapsed]="item?.collapsed">
<sb-accordion-header class="sb-bg-color-gray-0" (click)="isModuleExpanded = true;" tabindex="0">
<i class="icon-svg icon-svg--sm icon-tick mr-8" role="img" aria-label="tick-icon"
*ngIf="enrolledCourse && item?.consumedContent === item?.contentCount">
<svg class="icon">
<use xlink:href="./assets/images/sprite.svg#circle-with-check-symbol"></use>
</svg>
</i>
<div class="progress-circle progress-circle--sm mr-8" [attr.data-percentage]="item?.progress"
*ngIf="enrolledCourse && item?.consumedContent > 0 && item?.consumedContent !== item?.contentCount">
<svg class="progress-circle__svg" viewport="0 0 2000 2000">
<circle class="progress-circle__stroke" r="50%" cx="50%" cy="50%"></circle>
<circle class="progress-circle__stroke" r="50%" cx="50%" cy="50%"></circle>
</svg>
</div>
{{item?.name}}
</sb-accordion-header>
<sb-accordion-body>
<div class="chapter-box">
<div *ngIf="item;else noContent">
<div
*ngIf="item?.mimeType !== 'application/vnd.ekstep.content-collection' && !item?.children?.length">
<div class="child-content-padding">
<sb-toc-card [content]="item" (tocCardClick)="navigateToContent($event, item, 'toc-card')"
[type]="cardType" [contentStatus]="contentStatus" [maxAttempts]="item?.maxAttempts"
[scoreLabel]="'Best Score'" [disabled]="'disabled-toc-card'">
</sb-toc-card>
</div>
</div>
<div *ngIf="item?.children?.length">
<div *ngFor="let child of item?.children">
<!-- toc card for Non Self Assess content -->
<sb-toc-child-item
*ngIf="child?.contentType !== 'SelfAssess' && child?.mimeType !== 'application/vnd.sunbird.questionset'"
[childData]="child" class="sb-toc-child-item"
(tocCardClick)="navigateToContent($event, item, 'child-item')"
[contentStatus]="contentStatus" [type]="cardType">
</sb-toc-child-item>
<!-- toc card for Self Assess content with Label for Best Score -->
<sb-toc-child-item
*ngIf="child?.contentType === 'SelfAssess' || child?.mimeType === 'application/vnd.sunbird.questionset'"
[childData]="child" class="sb-toc-child-item"
(tocCardClick)="navigateToContent($event, item, 'child-item')"
[contentStatus]="contentStatus" [type]="cardType" [maxAttempts]="child?.maxAttempts"
[scoreLabel]="'Best Score'" [disabled]="'disabled-toc-card'">
</sb-toc-child-item>
</div>
</div>
</div>
<ng-template #noContent>
<div class="heading">{{noContentMessage}}</div>
</ng-template>
</div>
</sb-accordion-body>
</sb-accordion-item>
</sb-accordion>
</div>
</div>
<!-- Course details displayed on bottom when course is enrolled -->
<app-course-details [courseHierarchy]="courseHierarchy" *ngIf="enrolledCourse"></app-course-details>
</div>
<div class="sb-course-details__info sb-g-col-xs-12 sb-g-col-md-3 sb-g-col-lg-3 sb-g-col-xxxl-4">
<!-- <div class="sb-bg-color-white p-8 mb-16 computer only" *ngIf="enrolledBatchInfo?.cert_templates; else noCertificate">
<div class="certified-course__certificate d-inline-flex flex-ai-center py-8 px-16 width-100">
<img src="assets/images/certificate-icon.png" alt="Certificate">
<span
class="fnormal sb-color-primary ml-16 font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseContainCertificate}}</span>
</div>
</div>
<ng-template #noCertificate>
<div class="sb-bg-color-white p-8 mb-16 computer only">
<div class="certified-course__certificate d-inline-flex flex-ai-center py-8 px-16 width-100">
<img src="assets/images/certificate-icon.png" alt="Certificate">
<span
class="fnormal sb-color-primary ml-16 font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseDontContainCertificate}}</span>
</div>
</div>
</ng-template> -->
<div *ngIf="isEnrolledCourseUpdated" class="computer only">
<div class="sb-no-course-found my-16 d-flex flex-ai-center">
<div><img src="assets/images/alert.svg" alt="alert-image" width="20px" height="20px"></div>
<div class="ml-8">{{ generaliseLabelService?.frmelmnts?.lbl?.courseLastUpdatedOn }} {{
courseHierarchy?.lastUpdatedOn | date:'dd/MM/yyyy' }}</div>
</div>
</div>
<div class="sb-bg-color-white p-8 mb-16 computer only sbt-certified-course"
*ngIf="enrolledCourse && certificateDescription && !certificateDescription?.isCertificate">
<div class="certified-course__certificate d-inline-flex flex-ai-center py-8 px-16 width-100">
<img src="assets/images/certificate-icon.png" alt="Certificate">
<span
class="fnormal sb-color-primary ml-16 font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseDontContainCertificate}}</span>
</div>
</div>
<div class="sb-bg-color-white p-8 mb-16 computer only"
*ngIf="enrolledCourse && certificateDescription && certificateDescription?.description">
<div class="certified-course__certificate d-inline-flex flex-ai-center p-8 width-100">
<img src="assets/images/certificate-icon.png" alt="Certificate">
<span
class="fnormal sb-color-primary ml-16 font-weight-bold">{{generaliseLabelService?.frmelmnts?.lbl?.courseContainCertificate}}</span>
</div>
<p class="fsmall px-4 pt-8"> {{certificateDescription?.description}} </p>
</div>
<!-- 2. Course Progress bar -->
<div class="sb-bg-color-white certified-course__progress p-16 mb-16"
*ngIf="enrolledCourse && !addToGroup && !courseMentor">
<div class="fsmall relative9 relative">
<div class="kabab-menu pull-right" *ngIf="progressToDisplay === 100 && showForceSync && isConnected"
(click)="dropdownMenu();"></div>
<div class="kabab-menu-dropdown-content" [hidden]="dropdownContent">
<div class="d-flex flex-ai-center list p-8 w-100" (click)="forceSync()">
{{resourceService?.frmelmnts?.lbl?.forceSync}}</div>
</div>
</div>
<div class="fsmall">{{resourceService?.frmelmnts?.lbl?.courseProgress}}</div>
<div class="sb-color-primary fnormal font-weight-bold mt-8">{{progressToDisplay}}%
<span>{{resourceService?.frmelmnts?.lbl?.completed}}</span></div>
<mat-progress-bar mode="determinate" [value]="progressToDisplay" class="mb-0 mr-0 mt-8 tiny"></mat-progress-bar>
</div>
<!-- 3. Join batch, leave batch and show batch popup-->
<app-batch-details *ngIf="courseStatus !== 'Unlisted'" [courseId]="courseId" [batchId]="batchId"
[enrolledCourse]="enrolledCourse" [enrolledBatchInfo]="enrolledBatchInfo" [courseHierarchy]="courseHierarchy"
[courseProgressData]="courseProgressData" (allBatchDetails)="getAllBatchDetails($event)"></app-batch-details>
<!-- Consent to share PII -->
<div class="mb-16" *ngIf="showDataSettingSection && isConnected">
<app-global-consent-pii [collection]="courseHierarchy" [isglobalConsent]="false" [type]="courseConsent"
[consentConfig]="consentConfig"></app-global-consent-pii>
</div>
<!-- Credits & License information -->
<div class="course-player--metadata text-left">
<app-course-info [courseHierarchy]="courseHierarchy"></app-course-info>
</div>
</div>
</div>
</div>
</div>
<router-outlet></router-outlet>
<app-modal-wrapper [config]="{disableClose: false, size: 'small'}" *ngIf="showJoinTrainingModal"
(dismiss)="showJoinTrainingModal = false; logTelemetry('join-training-popup-close')">
<ng-template sbModalContent let-data>
<div class="sb-modal">
<div class="transition ui dimmer page modals active visible">
<div class="ui modal transition active visible small">
<button aria-label="close dialog" mat-dialog-close class="mat-close-btn">
<span>×</span>
</button>
<!--Header-->
<div class="sb-modal-header">
{{generaliseLabelService?.frmelmnts?.btn?.enroll}}
</div>
<!--/Header-->
<!--Content-->
<div class="sb-modal-content">
<div class="ui center aligned segment">
<p>{{batchMessage}}</p>
</div>
</div>
<!--/Content-->
</div>
</div>
</div>
</ng-template>
</app-modal-wrapper>
<app-modal-wrapper *ngIf="shareLinkModal" [config]="{disableClose: false, panelClass: 'material-modal'}"
(dismiss)="closeSharePopup('close-share-link-popup')">
<ng-template sbModalContent>
<app-share-link [shareLink]="shareLink" [telemetryShareData]="telemetryShareData">
</app-share-link>
</ng-template>
</app-modal-wrapper>
<app-confirmation-popup *ngIf="showConfirmationPopup" [popupMode]="popupMode" [batchId]="createdBatchId"
(close)="onPopupClose($event)"></app-confirmation-popup>
<!-- new UI -->
<div class="sb-bg-color-gray-0 textbook-container hide">
<div class="ui container">
<div class="textbook py-16 d-flex flex-ai-center">
<div class="textbook__details d-flex flex-ai-center">
<img src="./assets/images/book.png" class="textbook__bookimg" alt="">
<div class="ml-8 textbook__heading">
<h5 class="textbook__title sb-color-primary font-weight-bold mb-0" tabindex="0">TN X SCIENCE EM ALL ONE MARKS
COLLECTIONS</h5>
<div class="textbook__rating">
<sui-rating class="star mini" [ngModel]="4" [maximum]="5"></sui-rating>
</div>
</div>
</div>
<button class="sb-btn sb-btn-secondary sb-btn-normal ml-auto textbook__addbtn">Add to Group</button>
</div>
</div>
</div>
<app-course-completion *ngIf="showCourseCompleteMessage" [certificateDescription]="certificateDescription"
(close)="onCourseCompleteClose()"></app-course-completion>
<!-- Start - Modal for Assessment attempts -->
<app-modal-wrapper *ngIf="showLastAttemptsModal" [config]="{disableClose: false, size: 'small'}" #modal>
<ng-template sbModalContent>
<div class="sb-modal">
<div class="transition ui dimmer page modals active visible">
<div class="ui modal transition active visible small">
<div class="sb-modal-content sb-join-modal-content">
<div class="sb-h4 px-0 py-20">
{{resourceService?.frmelmnts?.lbl?.selfAssessLastAttempt}}
</div>
</div>
<div class="sb-modal-actions">
<button class="sb-btn sb-btn-normal sb-btn-primary" tabindex="0"
(click)="showLastAttemptsModal = false; _navigateToContent();">
{{resourceService.frmelmnts?.btn?.ok}}
</button>
<button class="sb-btn sb-btn-normal sb-btn-outline-primary" tabindex="0"
(click)="showLastAttemptsModal = false;">
{{resourceService.frmelmnts?.btn?.cancel}}
</button>
</div>
</div>
</div>
</div>
</ng-template>
</app-modal-wrapper>
<!-- End - Modal for Assessment attempts -->
course-player.component.scss
@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;
@use "pages/course-player" as *;
@use "pages/kabab-menu" as *;
@use 'pages/content-header' as *;
.course-list_title {
color: var(--gray-800);
font-size: calculateRem(14px);
font-weight: bold;
line-height: calculateRem(24px);
}
.course-list-progress {
padding: calculateRem(16px);
&__title {
color: var(--primary-theme-contrast);
font-size: calculateRem(12px);
}
&__status {
color: var(--primary-color);
font-size: calculateRem(14px);
font-weight: bold;
}
&__barholder {
margin: 0px;
height: calculateRem(8px);
}
&__info {
color: var(--gray-800);
font-size: calculateRem(12px);
}
}
:host ::ng-deep {
.ui.progress .bar {
height: calculateRem(8px) !important;
background-color: var(--secondary-200) !important;
}
}
.header-shadow {
min-height: calculateRem(80px);
background-color: var(--gray-0);
box-shadow: 0 calculateRem(10px) calculateRem(10px) calculateRem(-10px) rgba(var(--rc-rgba-black), 0.25);
z-index: 99;
}
.chapter-box {
border-bottom: .0625rem solid var(--gray-100);
border-radius: calculateRem(2px);
&:last-child{
border-bottom: 0px solid var(--gray-100);
padding-bottom:0px;
}
}
.sb-course-details {
.certified-course__certificate {
background-color: var(--rc-E0F1FD);
}
}
:host::ng-deep {
.batch-details {
&__dropdown {
.selection {
&.ui.dropdown {
color: var(--primary-color);
&:not(.button) > .default.text {
padding: calculateRem(6px) 0;
color: var(--primary-color);
}
}
&.ui.selection.dropdown > .dropdown.icon, .ui.selection.dropdown > .search.icon {
font-size: calculateRem(16px);
padding: calculateRem(8px);
}
}
}
}
.certified-course__progress {
.ui.progress .bar {
height: calculateRem(8px) !important;
background: var(--secondary-color);
}
}
.sbaccordion.sbaccordion--toc {
.sbaccordion__panel-header__title {
display: flex !important;
align-items: center;
padding: calculateRem(8px) 0 !important;
}
}
}
@include respond-below(sm) {
.certified-course__btn {
margin-top: calculateRem(8px);
}
}
// Progress Circle
$progressCircle:
xs calculateRem(16px) 4px 10px,
sm calculateRem(24px) 6px 10px,
md calculateRem(40px) 12px 10px,
lg calculateRem(56px) 16px 14px,
xl calculateRem(72px) 20px 18px,
xxl calculateRem(88px) 24px 20px;
@each $size, $shapeSize, $strokeSize, $fontSize in $progressCircle {
.progress-circle--#{$size} {
height: $shapeSize;
width: $shapeSize;
.progress-circle__stroke {
stroke-width: calculateRem($strokeSize);
}
span {
font-size: calculateRem($fontSize);
}
}
}
.progress-circle {
height: calculateRem(32px);
width: calculateRem(32px);
position: relative;
span {
position: absolute;
font-size: calculateRem(8px);
font-weight: bold;
margin: 0 auto;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&__svg {
border-radius: 50%;
height: 100%;
transform: scaleX(-1) rotate(-90deg);
width: 100%;
}
&__stroke {
fill: none;
stroke-width: calculateRem(8px);
&:nth-child(1) {
stroke: var(--secondary-200);
}
&:nth-child(2) {
stroke: var(--gray-100);
stroke-dasharray: 314.1592%;
}
}
&--xs, &--sm {
span {
display: none;
}
}
@each $size, $shapeSize, $strokeSize, $fontSize in $progressCircle {
&.progress-circle--#{$size} {
height: #{$shapeSize};
width: #{$shapeSize};
.progress-circle__stroke {
stroke-width: calculateRem($strokeSize);
}
span {
font-size: calculateRem($fontSize);
}
}
}
@for $i from 0 to 100 {
&[data-percentage="#{$i}"] {
.progress-circle__stroke:nth-child(2) {
stroke-dashoffset: calc(314.1592% * (#{$i} / 100));
}
}
}
}
.sb-no-course-found{
background-color: var(--tertiary-0);
border: calculateRem(1px) solid var(--tertiary-100);
padding: calculateRem(16px) calculateRem(24px);
border-radius: calculateRem(4px);
font-size: calculateRem(12px);
&__title{
color:var(--gray-800)
}
}