File

src/app/modules/learn/components/course-consumption/course-player/course-player.component.ts

Implements

OnInit OnDestroy

Metadata

Index

Properties
Methods

Constructor

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 :
Name Type Optional
activatedRoute ActivatedRoute No
configService ConfigService No
courseConsumptionService CourseConsumptionService No
windowScrollService WindowScrollService No
router Router No
navigationHelperService NavigationHelperService No
userService UserService No
toasterService ToasterService No
resourceService ResourceService No
popupControlService PopupControlService No
courseBatchService CourseBatchService No
permissionService PermissionService No
externalUrlPreviewService ExternalUrlPreviewService No
coursesService CoursesService No
courseProgressService CourseProgressService No
deviceDetectorService DeviceDetectorService No
telemetryService TelemetryService No
contentUtilsServiceService ContentUtilsServiceService No
layoutService LayoutService No
generaliseLabelService GeneraliseLabelService No
connectionService ConnectionService No
CsCourseService CsCourseService No
notificationService NotificationServiceImpl No

Methods

Private _navigateToContent
_navigateToContent()
Returns : void
calculateProgress
calculateProgress()
Returns : void
closeSharePopup
closeSharePopup(id)
Parameters :
Name Optional
id No
Returns : void
collapsedChange
collapsedChange(event: boolean, index: number)
Parameters :
Name Type Optional
event boolean No
index number No
Returns : void
dropdownMenu
dropdownMenu()
Returns : void
Public findContentById
findContentById(id: string)
Parameters :
Name Type Optional
id string No
Returns : any
Public forceSync
forceSync()
Returns : void
getAllBatchDetails
getAllBatchDetails(event)
Parameters :
Name Optional
event No
Returns : void
Private getContentState
getContentState()
Returns : void
getDataSetting
getDataSetting()
Returns : boolean
initLayout
initLayout()
Returns : void
isCourseModifiedAfterEnrolment
isCourseModifiedAfterEnrolment()
Returns : void
isEnrollmentAllowed
isEnrollmentAllowed(enrollmentEndDate)
Parameters :
Name Optional
enrollmentEndDate No
Returns : any
isExpanded
isExpanded(index: number)
Parameters :
Name Type Optional
index number No
Returns : boolean
logTelemetry
logTelemetry(id, content?: literal type)
Parameters :
Name Type Optional
id No
content literal type Yes
Returns : void
navigateToConfigureCertificate
navigateToConfigureCertificate(mode: string, batchId: string)
Parameters :
Name Type Optional
mode string No
batchId string No
Returns : void
Public navigateToContent
navigateToContent(event: any, collectionUnit?: any, id?)
Parameters :
Name Type Optional
event any No
collectionUnit any Yes
id Yes
Returns : void
navigateToPlayerPage
navigateToPlayerPage(collectionUnit: any, event?)
Parameters :
Name Type Optional
collectionUnit any No
event Yes
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
onCourseCompleteClose
onCourseCompleteClose()
Returns : void
onPopupClose
onPopupClose(event)
           telemetry event based on the event mode.
Parameters :
Name Optional
event No
Returns : void
Private parseChildContent
parseChildContent()
Returns : void
Private setTelemetryCourseImpression
setTelemetryCourseImpression()
Returns : void
setTelemetryShareData
setTelemetryShareData(param)
Parameters :
Name Optional
param No
Returns : void
Private setTelemetryStartEndData
setTelemetryStartEndData()
Returns : void
shareUnitLink
shareUnitLink(unit: any)
Parameters :
Name Type Optional
unit any No
Returns : void
validateBatchDate
validateBatchDate(batch)
Parameters :
Name Optional
batch No
Returns : any

Properties

_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>&times;</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) 
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""