@sunbird-cb/toc 0.36.2 → 0.36.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1254,10 +1254,10 @@ class AppTocService {
1254
1254
  */
1255
1255
  safeCompletionStatus(value) {
1256
1256
  const numValue = Number(value);
1257
- if (isNaN(numValue) || typeof value === 'string') {
1257
+ if (isNaN(numValue)) {
1258
1258
  return 0;
1259
1259
  }
1260
- return numValue;
1260
+ return Math.min(Math.max(Math.round(numValue), 0), 2);
1261
1261
  }
1262
1262
  constructor(http, contentLangSvc, configSvc, widgetSvc) {
1263
1263
  this.http = http;
@@ -2126,9 +2126,8 @@ class AppTocService {
2126
2126
  };
2127
2127
  }
2128
2128
  this.createHirarchyProgressHashmap(hierarchyData, rootCourseCategory);
2129
- // NOTE: computeMilestoneLockingStatus is called AFTER progress data is populated
2130
- // in mapCompletionPercentageProgram, not here where completion data is still 0
2131
2129
  this.hashmap = { ...this.hashmap };
2130
+ this.hashmapUpdated.next({ timestamp: Date.now(), hashmap: this.hashmap });
2132
2131
  }
2133
2132
  }
2134
2133
  computeMilestoneLockingStatus(isEnrolled = true) {
@@ -9555,10 +9554,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
9555
9554
 
9556
9555
  // import { NsAppToc } from '../models/app-toc.model'
9557
9556
  class AppTocContentComponent {
9558
- constructor(route, tocSvc, configSvc) {
9557
+ constructor(route, tocSvc, configSvc, cdr) {
9559
9558
  this.route = route;
9560
9559
  this.tocSvc = tocSvc;
9561
9560
  this.configSvc = configSvc;
9561
+ this.cdr = cdr;
9562
9562
  this.forPreview = false;
9563
9563
  this.resumeData = null;
9564
9564
  this.batchData = null;
@@ -9627,6 +9627,7 @@ class AppTocContentComponent {
9627
9627
  if (update && update.hashmap) {
9628
9628
  // Update hierarchyMapData with the latest hashmap from the service
9629
9629
  this.hierarchyMapData = update.hashmap;
9630
+ this.cdr.detectChanges();
9630
9631
  }
9631
9632
  });
9632
9633
  const instanceConfig = this.configSvc.instanceConfig;
@@ -9810,13 +9811,13 @@ class AppTocContentComponent {
9810
9811
  return false;
9811
9812
  return element.scrollHeight > element.clientHeight;
9812
9813
  }
9813
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppTocContentComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: AppTocService }, { token: i2$1.ConfigurationsService }], target: i0.ɵɵFactoryTarget.Component }); }
9814
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppTocContentComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: AppTocService }, { token: i2$1.ConfigurationsService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
9814
9815
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: AppTocContentComponent, selector: "ws-widget-app-toc-content", inputs: { batchId: "batchId", content: "content", forPreview: "forPreview", resumeData: "resumeData", batchData: "batchData", skeletonLoader: "skeletonLoader", tocStructure: "tocStructure", config: "config", hierarchyMapData: "hierarchyMapData", pathSet: "pathSet", componentName: "componentName", isPreAssessment: "isPreAssessment", baseContentReadData: "baseContentReadData", contentReadData: "contentReadData" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"content?.courseCategory === 'Learning Pathway' && componentName !== 'viewer'\">\n <!-- Milestone / Learning Path UI -->\n <div class=\"milestone-wrapper\" role=\"group\" aria-label=\"Learning path milestones\">\n <!-- Learning Path Header -->\n <div class=\"learning-path-header\">\n <h3 class=\"learning-path-title\">Learning Path</h3>\n <p class=\"learning-path-subtitle\">Complete {{content?.children?.length - 1 || 3}} key milestones</p>\n </div>\n <ng-container *ngIf=\"content?.children?.length; else fallbackMilestones\">\n <ul class=\"milestone-list\">\n <li *ngFor=\"let m of content?.children; let i = index\" class=\"milestone-item\"\n [class.completed]=\"m?.completionStatus === 2 || getMilestoneCompletedOrNot(m?.identifier)\" role=\"listitem\">\n <div class=\"milestone-circle\" [attr.aria-label]=\"m?.name || ('Milestone ' + (i+1))\">\n <img *ngIf=\"m?.completionStatus === 2 || getMilestoneCompletedOrNot(m?.identifier)\" src=\"assets/icons/toc/tick-orange.svg\"\n class=\"milestone-checkmark\" alt=\"Completed\" />\n <span *ngIf=\"m?.completionStatus !== 2 && !getMilestoneCompletedOrNot(m?.identifier)\" class=\"milestone-index\"></span>\n </div>\n <div class=\"milestone-label\">\n <p class=\"mb-0 leading-none\">\n <span *ngIf=\"m?.contextCategory\">{{m?.contextCategory}}</span>\n <span *ngIf=\"m?.courseCategory\">{{m?.courseCategory}} {{ i}}</span>\n </p>\n <p #milestoneNameRef class=\"milestone-name-text leading-none\"\n [matTooltip]=\"isMultiLineTruncated(milestoneNameRef) ? m?.name : ''\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"multiline-tooltip\">\n {{ m?.name }}\n </p>\n </div>\n </li>\n </ul>\n </ng-container>\n <ng-template #fallbackMilestones>\n <ul class=\"milestone-list\">\n <li class=\"milestone-item completed\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"Pre Assessment\">\n <img src=\"assets/icons/toc/tick-orange.svg\" class=\"milestone-checkmark\" alt=\"Completed\" />\n </div>\n <div class=\"milestone-label\">Pre Assessment</div>\n </li>\n <li class=\"milestone-item completed\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"1. Foundation of Workplace Ethics\">\n <img src=\"assets/icons/toc/tick-orange.svg\" class=\"milestone-checkmark\" alt=\"Completed\" />\n </div>\n <div class=\"milestone-label\">1. Foundation of Workplace Ethics</div>\n </li>\n <li class=\"milestone-item\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"2. Health and Productivity at Work\">\n <img src=\"assets/icons/toc/tick-orange.svg\" class=\"milestone-checkmark\" alt=\"Completed\" />\n </div>\n <div class=\"milestone-label\">2. Health and Productivity at Work</div>\n </li>\n <li class=\"milestone-item\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"3. Health and Productivity at Work\">\n <span class=\"milestone-index\">4</span>\n </div>\n <div class=\"milestone-label\">3. Health and Productivity at Work</div>\n </li>\n </ul>\n </ng-template>\n </div>\n</div>\n<div class=\"bggrey\">\n <div class=\"py-4 content-container\" id=\"contentContainer\" *ngIf=\"!isPreAssessment\">\n <div class=\"main-container flex flex-col gap-4\">\n <ng-container *ngIf=\"content?.primaryCategory === nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <div class=\"padding-left-m flex gap-4 tab-list\">\n <ng-container *ngFor=\"let type of typesOfContent\">\n <div class=\"tab-data\" (click)=\"!type.disabled && selectedTabType = type.id\"\n [ngClass]=\"{'active-tab': selectedTabType === type.id, 'disabled':type.disabled}\">\n <span>{{type.name}}</span>\n </div>\n </ng-container>\n </div>\n </ng-container>\n <ng-container\n *ngIf=\"selectedTabType === 'content' && content?.primaryCategory === nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <ng-container [ngTemplateOutlet]=\"contentBlock\"></ng-container>\n </ng-container>\n <ng-container\n *ngIf=\"selectedTabType === 'session' && content?.primaryCategory === nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <ws-widget-app-app-toc-sessions-new [batchData]=\"batchData\" [pathSet]=\"pathSet\" [rootId]=\"contextId\"\n [rootContentType]=\"contextPath\" [batchId]=\"batchId\" [config]=\"config\" [content]=\"content\">\n </ws-widget-app-app-toc-sessions-new>\n </ng-container>\n <ng-container *ngIf=\"content?.primaryCategory !== nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <ng-container [ngTemplateOutlet]=\"contentBlock\"></ng-container>\n </ng-container>\n </div>\n </div>\n <!-- {{pathSet|json}} -->\n <ng-template #contentBlock>\n <ng-container *ngIf=\"!contentLoader\">\n <ng-container *ngIf=\"content?.children?.length; else noChildren\">\n <div class=\"content-card \"\n *ngFor=\"let card of content?.children; trackBy: contentTrackBy; let i = index;\"\n [ngClass]=\"{ 'content-active': pathSet && pathSet.has(card.identifier) && isEnrolled}\">\n <!-- {{card|json}} -->\n <ws-widget-app-toc-content-card-v2 [forPreview]=\"forPreview\" [expandAll]=\"expandAll\"\n [componentName]=\"componentName\" [expandActive]=\"true\" [rootId]=\"contextId\" [batchId]=\"batchId\"\n [rootContentType]=\"contextPath\" [content]=\"card\" [mlCourse]=\"content\" [index]=\"i+1\"\n [pathSet]=\"pathSet\" [hierarchyMapData]=\"hierarchyMapData\" [batchData]=\"batchData\"\n [baseContentReadData]=\"baseContentReadData\"></ws-widget-app-toc-content-card-v2>\n </div>\n </ng-container>\n <ng-template #noChildren>\n <div *ngIf=\"!isPlayable\"\n class=\"ws-mat-primary-default-text ws-mat-primary-lite-background px-6 py-8 font-normal text-base noChildren\">\n The preview for this type of content is unavailable. Please click on Start button above to view.\n </div>\n <!-- <ng-container *ngIf=\"isPlayable\" [sbUiResolverWidget]=\"contentPlayWidgetConfig\"></ng-container> -->\n </ng-template>\n </ng-container>\n\n <ng-container *ngIf=\"contentLoader\">\n <ws-widget-app-toc-content-card-v2-skeleton></ws-widget-app-toc-content-card-v2-skeleton>\n </ng-container>\n </ng-template>\n <div *ngIf=\"isPreAssessment\">\n <div class=\"content-card \"\n *ngFor=\"let card of content?.preEnrolmentResources; trackBy: identifier; let i = index;\"\n [ngClass]=\"{ 'content-active': pathSet && pathSet.has(card.identifier) && isEnrolled}\">\n <!-- {{card|json}} -->\n <ws-widget-app-toc-content-card-v2 [forPreview]=\"forPreview\" [expandAll]=\"expandAll\"\n [componentName]=\"componentName\" [expandActive]=\"true\" [rootId]=\"contextId\" [batchId]=\"batchId\"\n [rootContentType]=\"contextPath\" [isPreAssessment]=\"isPreAssessment\" [content]=\"card\"\n [mlCourse]=\"content\" [index]=\"i+1\" [pathSet]=\"pathSet\" [hierarchyMapData]=\"hierarchyMapData\"\n [batchData]=\"batchData\" [baseContentReadData]=\"baseContentReadData\"></ws-widget-app-toc-content-card-v2>\n </div>\n </div>\n</div>", styles: [".bggrey{background-color:#eff3f9}.content-container{min-height:800px}.content-accordion .content-panel{box-shadow:none}.content-accordion .content-header{padding:20px;background-color:#fff;border:none;box-shadow:none}.content-key-values{color:#0009}.content-key-values mat-icon{width:14px;height:14px;font-size:14px;color:#0006}.main-container{width:100%;box-sizing:border-box}.main-container .toc-section{display:flex;justify-content:space-between;padding-bottom:12px}.main-container .content-card{background:#fff;border:1px solid rgb(221,221,221);overflow:hidden;position:relative}.main-container .content-card.content-active{border:1px solid #1a4ca1}.main-container .noChildren{color:#222!important}@media only screen and (max-width: 959px){.main-container{width:100%;padding-right:0;margin-bottom:32px}}::ng-deep .mat-expansion-indicator{display:none!important}.mat-panel-indicator{width:28px;height:28px;font-size:28px}.tab-list .tab-data{border-radius:18px;border:1px solid rgba(0,0,0,.08);opacity:1;background-color:#e5e9ef;padding:6px 16px;cursor:pointer}.tab-list .tab-data span{opacity:1;color:#000000de;font-family:Lato-Regular;font-size:14px;font-weight:400;font-style:Regular;letter-spacing:.25px;text-align:left;line-height:20px}.tab-list .active-tab{border-radius:18px;border:1px solid rgb(27,76,161);opacity:1;background-color:#dee6f2}.tab-list .active-tab span{color:#1b4ca1}.tab-list .disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.milestone-wrapper{--milestone-size: 33px;--milestone-border: 3px;padding:16px;background:#fff;display:flex;align-items:center;margin-top:16px}.learning-path-header{padding-right:16px}.learning-path-title{font-size:18px;font-weight:700;color:#000000de;margin:0 0 4px;font-family:Lato-Bold,Lato,sans-serif}.learning-path-subtitle{font-size:14px;font-weight:400;color:#0009;margin:0;font-family:Lato-Regular,Lato,sans-serif}.milestone-list{display:flex;align-items:flex-start;justify-content:space-between;list-style:none;margin:0;gap:12px;flex:1}.milestone-item{display:flex;flex-direction:column;align-items:center;text-align:center;flex:1 1 0;position:relative}.milestone-item:after{content:\"\";position:absolute;top:calc(var(--milestone-size) / 2);left:50%;right:-50%;height:3px;background:#e0e0e0;border-top:3px dotted #BDBDBD;background:transparent;z-index:0}.milestone-item.completed:after{background:linear-gradient(90deg,#efa34f,#f3962f);border:none}.milestone-item:last-child:after{display:none}.milestone-circle{width:var(--milestone-size);height:var(--milestone-size);min-width:var(--milestone-size);border-radius:50%;background:#fff;border:var(--milestone-border) solid #F3962F;display:flex;align-items:center;justify-content:center;z-index:1;position:relative;box-shadow:0 2px 8px #00000014;transition:all .2s ease}.milestone-item.completed .milestone-circle{background:#fff;border-color:#f3962f;box-shadow:0 4px 12px #efa34f4d}.milestone-checkmark{width:80%;height:80%;object-fit:contain}.milestone-index{font-weight:700;font-size:18px;color:#757575}.milestone-item.completed .milestone-index{color:#fff}.milestone-label{margin-top:12px;font-size:14px;line-height:1.4;color:#000000de;word-wrap:break-word;white-space:normal;font-weight:500}.milestone-item:focus-within .milestone-circle,.milestone-item:hover .milestone-circle{transform:translateY(-2px) scale(1.05);box-shadow:0 6px 16px #00000026}.milestone-item.completed:hover .milestone-circle{box-shadow:0 6px 20px #efa34f66}@media (max-width: 768px){.milestone-wrapper{--milestone-size: 48px;padding:20px 12px 28px}.milestone-list{gap:8px;padding:0 8px}.milestone-label{font-size:12px;max-width:100px}.milestone-index{font-size:16px}}@media (max-width: 480px){.milestone-wrapper{--milestone-size: 40px;padding:16px 8px 24px}.milestone-list{gap:6px;padding:0 4px}.milestone-label{font-size:11px;max-width:80px;margin-top:8px}.milestone-index{font-size:14px}.milestone-item:after{height:2px;border-top-width:2px}}.milestone-name-text{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;line-height:1.4;word-break:break-word}::ng-deep .multiline-tooltip{white-space:pre-wrap!important;max-width:400px!important;word-break:break-word!important;line-height:1.4!important}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5$2.MatLegacyTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { kind: "component", type: AppTocContentCardV2Component, selector: "ws-widget-app-toc-content-card-v2", inputs: ["content", "expandAll", "rootId", "rootContentType", "forPreview", "batchId", "componentName", "index", "pathSet", "expandActive", "hierarchyMapData", "batchData", "isPreAssessment", "baseContentReadData", "mlCourse", "parentMilestoneLocked"] }, { kind: "component", type: AppTocSessionsNewComponent, selector: "ws-widget-app-app-toc-sessions-new", inputs: ["batchData", "content", "forPreview", "config", "batchId", "rootId", "rootContentType", "pathSet"] }, { kind: "component", type: AppTocContentCardV2SkeletonComponent, selector: "ws-widget-app-toc-content-card-v2-skeleton" }] }); }
9815
9816
  }
9816
9817
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppTocContentComponent, decorators: [{
9817
9818
  type: Component,
9818
9819
  args: [{ selector: 'ws-widget-app-toc-content', template: "<div *ngIf=\"content?.courseCategory === 'Learning Pathway' && componentName !== 'viewer'\">\n <!-- Milestone / Learning Path UI -->\n <div class=\"milestone-wrapper\" role=\"group\" aria-label=\"Learning path milestones\">\n <!-- Learning Path Header -->\n <div class=\"learning-path-header\">\n <h3 class=\"learning-path-title\">Learning Path</h3>\n <p class=\"learning-path-subtitle\">Complete {{content?.children?.length - 1 || 3}} key milestones</p>\n </div>\n <ng-container *ngIf=\"content?.children?.length; else fallbackMilestones\">\n <ul class=\"milestone-list\">\n <li *ngFor=\"let m of content?.children; let i = index\" class=\"milestone-item\"\n [class.completed]=\"m?.completionStatus === 2 || getMilestoneCompletedOrNot(m?.identifier)\" role=\"listitem\">\n <div class=\"milestone-circle\" [attr.aria-label]=\"m?.name || ('Milestone ' + (i+1))\">\n <img *ngIf=\"m?.completionStatus === 2 || getMilestoneCompletedOrNot(m?.identifier)\" src=\"assets/icons/toc/tick-orange.svg\"\n class=\"milestone-checkmark\" alt=\"Completed\" />\n <span *ngIf=\"m?.completionStatus !== 2 && !getMilestoneCompletedOrNot(m?.identifier)\" class=\"milestone-index\"></span>\n </div>\n <div class=\"milestone-label\">\n <p class=\"mb-0 leading-none\">\n <span *ngIf=\"m?.contextCategory\">{{m?.contextCategory}}</span>\n <span *ngIf=\"m?.courseCategory\">{{m?.courseCategory}} {{ i}}</span>\n </p>\n <p #milestoneNameRef class=\"milestone-name-text leading-none\"\n [matTooltip]=\"isMultiLineTruncated(milestoneNameRef) ? m?.name : ''\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"multiline-tooltip\">\n {{ m?.name }}\n </p>\n </div>\n </li>\n </ul>\n </ng-container>\n <ng-template #fallbackMilestones>\n <ul class=\"milestone-list\">\n <li class=\"milestone-item completed\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"Pre Assessment\">\n <img src=\"assets/icons/toc/tick-orange.svg\" class=\"milestone-checkmark\" alt=\"Completed\" />\n </div>\n <div class=\"milestone-label\">Pre Assessment</div>\n </li>\n <li class=\"milestone-item completed\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"1. Foundation of Workplace Ethics\">\n <img src=\"assets/icons/toc/tick-orange.svg\" class=\"milestone-checkmark\" alt=\"Completed\" />\n </div>\n <div class=\"milestone-label\">1. Foundation of Workplace Ethics</div>\n </li>\n <li class=\"milestone-item\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"2. Health and Productivity at Work\">\n <img src=\"assets/icons/toc/tick-orange.svg\" class=\"milestone-checkmark\" alt=\"Completed\" />\n </div>\n <div class=\"milestone-label\">2. Health and Productivity at Work</div>\n </li>\n <li class=\"milestone-item\" role=\"listitem\">\n <div class=\"milestone-circle\" aria-label=\"3. Health and Productivity at Work\">\n <span class=\"milestone-index\">4</span>\n </div>\n <div class=\"milestone-label\">3. Health and Productivity at Work</div>\n </li>\n </ul>\n </ng-template>\n </div>\n</div>\n<div class=\"bggrey\">\n <div class=\"py-4 content-container\" id=\"contentContainer\" *ngIf=\"!isPreAssessment\">\n <div class=\"main-container flex flex-col gap-4\">\n <ng-container *ngIf=\"content?.primaryCategory === nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <div class=\"padding-left-m flex gap-4 tab-list\">\n <ng-container *ngFor=\"let type of typesOfContent\">\n <div class=\"tab-data\" (click)=\"!type.disabled && selectedTabType = type.id\"\n [ngClass]=\"{'active-tab': selectedTabType === type.id, 'disabled':type.disabled}\">\n <span>{{type.name}}</span>\n </div>\n </ng-container>\n </div>\n </ng-container>\n <ng-container\n *ngIf=\"selectedTabType === 'content' && content?.primaryCategory === nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <ng-container [ngTemplateOutlet]=\"contentBlock\"></ng-container>\n </ng-container>\n <ng-container\n *ngIf=\"selectedTabType === 'session' && content?.primaryCategory === nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <ws-widget-app-app-toc-sessions-new [batchData]=\"batchData\" [pathSet]=\"pathSet\" [rootId]=\"contextId\"\n [rootContentType]=\"contextPath\" [batchId]=\"batchId\" [config]=\"config\" [content]=\"content\">\n </ws-widget-app-app-toc-sessions-new>\n </ng-container>\n <ng-container *ngIf=\"content?.primaryCategory !== nsContent?.EPrimaryCategory?.BLENDED_PROGRAM\">\n <ng-container [ngTemplateOutlet]=\"contentBlock\"></ng-container>\n </ng-container>\n </div>\n </div>\n <!-- {{pathSet|json}} -->\n <ng-template #contentBlock>\n <ng-container *ngIf=\"!contentLoader\">\n <ng-container *ngIf=\"content?.children?.length; else noChildren\">\n <div class=\"content-card \"\n *ngFor=\"let card of content?.children; trackBy: contentTrackBy; let i = index;\"\n [ngClass]=\"{ 'content-active': pathSet && pathSet.has(card.identifier) && isEnrolled}\">\n <!-- {{card|json}} -->\n <ws-widget-app-toc-content-card-v2 [forPreview]=\"forPreview\" [expandAll]=\"expandAll\"\n [componentName]=\"componentName\" [expandActive]=\"true\" [rootId]=\"contextId\" [batchId]=\"batchId\"\n [rootContentType]=\"contextPath\" [content]=\"card\" [mlCourse]=\"content\" [index]=\"i+1\"\n [pathSet]=\"pathSet\" [hierarchyMapData]=\"hierarchyMapData\" [batchData]=\"batchData\"\n [baseContentReadData]=\"baseContentReadData\"></ws-widget-app-toc-content-card-v2>\n </div>\n </ng-container>\n <ng-template #noChildren>\n <div *ngIf=\"!isPlayable\"\n class=\"ws-mat-primary-default-text ws-mat-primary-lite-background px-6 py-8 font-normal text-base noChildren\">\n The preview for this type of content is unavailable. Please click on Start button above to view.\n </div>\n <!-- <ng-container *ngIf=\"isPlayable\" [sbUiResolverWidget]=\"contentPlayWidgetConfig\"></ng-container> -->\n </ng-template>\n </ng-container>\n\n <ng-container *ngIf=\"contentLoader\">\n <ws-widget-app-toc-content-card-v2-skeleton></ws-widget-app-toc-content-card-v2-skeleton>\n </ng-container>\n </ng-template>\n <div *ngIf=\"isPreAssessment\">\n <div class=\"content-card \"\n *ngFor=\"let card of content?.preEnrolmentResources; trackBy: identifier; let i = index;\"\n [ngClass]=\"{ 'content-active': pathSet && pathSet.has(card.identifier) && isEnrolled}\">\n <!-- {{card|json}} -->\n <ws-widget-app-toc-content-card-v2 [forPreview]=\"forPreview\" [expandAll]=\"expandAll\"\n [componentName]=\"componentName\" [expandActive]=\"true\" [rootId]=\"contextId\" [batchId]=\"batchId\"\n [rootContentType]=\"contextPath\" [isPreAssessment]=\"isPreAssessment\" [content]=\"card\"\n [mlCourse]=\"content\" [index]=\"i+1\" [pathSet]=\"pathSet\" [hierarchyMapData]=\"hierarchyMapData\"\n [batchData]=\"batchData\" [baseContentReadData]=\"baseContentReadData\"></ws-widget-app-toc-content-card-v2>\n </div>\n </div>\n</div>", styles: [".bggrey{background-color:#eff3f9}.content-container{min-height:800px}.content-accordion .content-panel{box-shadow:none}.content-accordion .content-header{padding:20px;background-color:#fff;border:none;box-shadow:none}.content-key-values{color:#0009}.content-key-values mat-icon{width:14px;height:14px;font-size:14px;color:#0006}.main-container{width:100%;box-sizing:border-box}.main-container .toc-section{display:flex;justify-content:space-between;padding-bottom:12px}.main-container .content-card{background:#fff;border:1px solid rgb(221,221,221);overflow:hidden;position:relative}.main-container .content-card.content-active{border:1px solid #1a4ca1}.main-container .noChildren{color:#222!important}@media only screen and (max-width: 959px){.main-container{width:100%;padding-right:0;margin-bottom:32px}}::ng-deep .mat-expansion-indicator{display:none!important}.mat-panel-indicator{width:28px;height:28px;font-size:28px}.tab-list .tab-data{border-radius:18px;border:1px solid rgba(0,0,0,.08);opacity:1;background-color:#e5e9ef;padding:6px 16px;cursor:pointer}.tab-list .tab-data span{opacity:1;color:#000000de;font-family:Lato-Regular;font-size:14px;font-weight:400;font-style:Regular;letter-spacing:.25px;text-align:left;line-height:20px}.tab-list .active-tab{border-radius:18px;border:1px solid rgb(27,76,161);opacity:1;background-color:#dee6f2}.tab-list .active-tab span{color:#1b4ca1}.tab-list .disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.milestone-wrapper{--milestone-size: 33px;--milestone-border: 3px;padding:16px;background:#fff;display:flex;align-items:center;margin-top:16px}.learning-path-header{padding-right:16px}.learning-path-title{font-size:18px;font-weight:700;color:#000000de;margin:0 0 4px;font-family:Lato-Bold,Lato,sans-serif}.learning-path-subtitle{font-size:14px;font-weight:400;color:#0009;margin:0;font-family:Lato-Regular,Lato,sans-serif}.milestone-list{display:flex;align-items:flex-start;justify-content:space-between;list-style:none;margin:0;gap:12px;flex:1}.milestone-item{display:flex;flex-direction:column;align-items:center;text-align:center;flex:1 1 0;position:relative}.milestone-item:after{content:\"\";position:absolute;top:calc(var(--milestone-size) / 2);left:50%;right:-50%;height:3px;background:#e0e0e0;border-top:3px dotted #BDBDBD;background:transparent;z-index:0}.milestone-item.completed:after{background:linear-gradient(90deg,#efa34f,#f3962f);border:none}.milestone-item:last-child:after{display:none}.milestone-circle{width:var(--milestone-size);height:var(--milestone-size);min-width:var(--milestone-size);border-radius:50%;background:#fff;border:var(--milestone-border) solid #F3962F;display:flex;align-items:center;justify-content:center;z-index:1;position:relative;box-shadow:0 2px 8px #00000014;transition:all .2s ease}.milestone-item.completed .milestone-circle{background:#fff;border-color:#f3962f;box-shadow:0 4px 12px #efa34f4d}.milestone-checkmark{width:80%;height:80%;object-fit:contain}.milestone-index{font-weight:700;font-size:18px;color:#757575}.milestone-item.completed .milestone-index{color:#fff}.milestone-label{margin-top:12px;font-size:14px;line-height:1.4;color:#000000de;word-wrap:break-word;white-space:normal;font-weight:500}.milestone-item:focus-within .milestone-circle,.milestone-item:hover .milestone-circle{transform:translateY(-2px) scale(1.05);box-shadow:0 6px 16px #00000026}.milestone-item.completed:hover .milestone-circle{box-shadow:0 6px 20px #efa34f66}@media (max-width: 768px){.milestone-wrapper{--milestone-size: 48px;padding:20px 12px 28px}.milestone-list{gap:8px;padding:0 8px}.milestone-label{font-size:12px;max-width:100px}.milestone-index{font-size:16px}}@media (max-width: 480px){.milestone-wrapper{--milestone-size: 40px;padding:16px 8px 24px}.milestone-list{gap:6px;padding:0 4px}.milestone-label{font-size:11px;max-width:80px;margin-top:8px}.milestone-index{font-size:14px}.milestone-item:after{height:2px;border-top-width:2px}}.milestone-name-text{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;line-height:1.4;word-break:break-word}::ng-deep .multiline-tooltip{white-space:pre-wrap!important;max-width:400px!important;word-break:break-word!important;line-height:1.4!important}\n"] }]
9819
- }], ctorParameters: function () { return [{ type: i1$1.ActivatedRoute }, { type: AppTocService }, { type: i2$1.ConfigurationsService }]; }, propDecorators: { batchId: [{
9820
+ }], ctorParameters: function () { return [{ type: i1$1.ActivatedRoute }, { type: AppTocService }, { type: i2$1.ConfigurationsService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { batchId: [{
9820
9821
  type: Input
9821
9822
  }], content: [{
9822
9823
  type: Input
@@ -13726,17 +13727,20 @@ class ViewerUtilService {
13726
13727
  this.http
13727
13728
  .patch(`${this.API_ENDPOINTS.PROGRESS_UPDATE}/${contentId}`, req)
13728
13729
  .subscribe(noop, noop);
13729
- if (this.tocSvc.hashmap[contentId]) {
13730
- const currentStatus = this.tocSvc.hashmap[contentId]['completionStatus'];
13731
- if (!currentStatus || currentStatus < 2) {
13732
- this.tocSvc.hashmap[contentId]['completionPercentage'] = req.request.contents[0].completionPercentage;
13733
- this.tocSvc.hashmap[contentId]['completionStatus'] = Number(req.request.contents[0].status) || 0;
13734
- this.tocSvc.hashmap = { ...this.tocSvc.hashmap };
13735
- // Trigger milestone lock recomputation - it will emit hashmapUpdated with both progress and lock changes
13736
- this.tocSvc.triggerMilestoneLockUpdate();
13737
- // Emit to trigger viewer component refresh for Learning Pathways
13738
- this.markAsCompleteSubject.next(true);
13739
- }
13730
+ const hashEntry = this.tocSvc.hashmap[contentId] || {};
13731
+ const currentStatus = hashEntry['completionStatus'];
13732
+ if (!currentStatus || currentStatus < 2) {
13733
+ this.tocSvc.hashmap[contentId] = {
13734
+ ...hashEntry,
13735
+ identifier: contentId,
13736
+ completionPercentage: req.request.contents[0].completionPercentage,
13737
+ completionStatus: Number(req.request.contents[0].status) || 0,
13738
+ };
13739
+ this.tocSvc.hashmap = { ...this.tocSvc.hashmap };
13740
+ // Trigger milestone lock recomputation - it will emit hashmapUpdated with both progress and lock changes
13741
+ this.tocSvc.triggerMilestoneLockUpdate();
13742
+ // Emit to trigger viewer component refresh for Learning Pathways
13743
+ this.markAsCompleteSubject.next(true);
13740
13744
  }
13741
13745
  }
13742
13746
  else {
@@ -13934,11 +13938,16 @@ class ViewerUtilService {
13934
13938
  this.http
13935
13939
  .patch(`${this.API_ENDPOINTS.PROGRESS_UPDATE}/${contentId}`, req)
13936
13940
  .subscribe(noop, noop);
13937
- if (this.tocSvc.hashmap && this.tocSvc.hashmap[contentId] && req.request.contents[0]) {
13938
- const currentStatus = this.tocSvc.hashmap[contentId]['completionStatus'];
13941
+ if (this.tocSvc.hashmap && req.request.contents[0]) {
13942
+ const hashEntry = this.tocSvc.hashmap[contentId] || {};
13943
+ const currentStatus = hashEntry['completionStatus'];
13939
13944
  if (!currentStatus || currentStatus < 2) {
13940
- this.tocSvc.hashmap[contentId]['completionPercentage'] = req.request.contents[0].completionPercentage;
13941
- this.tocSvc.hashmap[contentId]['completionStatus'] = Number(req.request.contents[0].status) || 0;
13945
+ this.tocSvc.hashmap[contentId] = {
13946
+ ...hashEntry,
13947
+ identifier: contentId,
13948
+ completionPercentage: req.request.contents[0].completionPercentage,
13949
+ completionStatus: Number(req.request.contents[0].status) || 0,
13950
+ };
13942
13951
  this.tocSvc.hashmap = { ...this.tocSvc.hashmap };
13943
13952
  // Trigger milestone lock recomputation - it will emit hashmapUpdated with both progress and lock changes
13944
13953
  if (this.tocSvc.triggerMilestoneLockUpdate) {
@@ -14010,23 +14019,26 @@ class ViewerUtilService {
14010
14019
  return this.http.get(`/apis/proxies/v8/course/v1/hierarchy/${id}?mode=${type}`);
14011
14020
  }
14012
14021
  updateContentHashMapForAssesstent(contentId, contentProgress) {
14013
- if (this.tocSvc.hashmap[contentId]) {
14014
- const currentStatus = this.tocSvc.hashmap[contentId]['completionStatus'] || 0;
14015
- const newStatus = Number(contentProgress.status) || 0;
14016
- // Update if not complete or if new status is higher
14017
- if (currentStatus < 2 || newStatus >= currentStatus) {
14018
- this.tocSvc.hashmap[contentId]['completionPercentage'] = contentProgress.completionPercentage;
14019
- this.tocSvc.hashmap[contentId]['completionStatus'] = newStatus;
14020
- this.tocSvc.hashmap[contentId]['status'] = newStatus;
14021
- // Create new hashmap reference for Angular change detection
14022
- this.tocSvc.hashmap = { ...this.tocSvc.hashmap };
14023
- // Trigger milestone lock recomputation - it will emit hashmapUpdated with both progress and lock changes
14024
- if (this.tocSvc.triggerMilestoneLockUpdate) {
14025
- this.tocSvc.triggerMilestoneLockUpdate();
14026
- }
14027
- // Emit to trigger viewer component refresh for Learning Pathways
14028
- this.markAsCompleteSubject.next(true);
14029
- }
14022
+ const hashEntry = this.tocSvc.hashmap[contentId] || {};
14023
+ const currentStatus = hashEntry['completionStatus'] || 0;
14024
+ const newStatus = Number(contentProgress.status) || 0;
14025
+ // Update if not complete or if new status is higher
14026
+ if (currentStatus < 2 || newStatus >= currentStatus) {
14027
+ this.tocSvc.hashmap[contentId] = {
14028
+ ...hashEntry,
14029
+ identifier: contentId,
14030
+ completionPercentage: contentProgress.completionPercentage,
14031
+ completionStatus: newStatus,
14032
+ status: newStatus,
14033
+ };
14034
+ // Create new hashmap reference for Angular change detection
14035
+ this.tocSvc.hashmap = { ...this.tocSvc.hashmap };
14036
+ // Trigger milestone lock recomputation - it will emit hashmapUpdated with both progress and lock changes
14037
+ if (this.tocSvc.triggerMilestoneLockUpdate) {
14038
+ this.tocSvc.triggerMilestoneLockUpdate();
14039
+ }
14040
+ // Emit to trigger viewer component refresh for Learning Pathways
14041
+ this.markAsCompleteSubject.next(true);
14030
14042
  }
14031
14043
  }
14032
14044
  getFormById(formId) {