@sunbird-cb/toc 0.0.11 → 0.0.12

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.
@@ -701,11 +701,11 @@ class WidgetContentService {
701
701
  const forPreview = window.location.href.includes('/public/') || window.location.href.includes('&preview=true');
702
702
  if (primaryCategory && this.isResource(primaryCategory)) {
703
703
  if (!forPreview) {
704
- url = `/apis/proxies/v8/extended/content/v1/read/${contentId}`;
704
+ url = `/apis/proxies/v8/action/content/v3/read/${contentId}`;
705
705
  }
706
706
  else {
707
707
  if (window.location.href.includes('editMode=true') && window.location.href.includes('_rc')) {
708
- url = `/apis/proxies/v8/extended/content/v1/read/${contentId}`;
708
+ url = `/apis/proxies/v8/action/content/v3/read/${contentId}`;
709
709
  }
710
710
  else {
711
711
  url = `/api/content/v1/read/${contentId}`;
@@ -1635,7 +1635,6 @@ class AppTocService {
1635
1635
  resetContentData() {
1636
1636
  this.hashmap = {};
1637
1637
  this.hashmapUpdated.next(null);
1638
- console.log('Content data cleared - hashmap reset');
1639
1638
  }
1640
1639
  fetchContentParents(contentId) {
1641
1640
  // return this.http.get<NsContent.IContentMinimal[]>(
@@ -2023,14 +2022,6 @@ class AppTocService {
2023
2022
  child.primaryCategory === 'Standalone Assessment' ||
2024
2023
  child.mimeType === 'application/vnd.sunbird.questionset';
2025
2024
  if (isAssessment) {
2026
- console.log(`šŸ“‹ Adding assessment to hashmap: "${child.name}"`, {
2027
- identifier: child.identifier,
2028
- primaryCategory: child.primaryCategory,
2029
- parent: correctParentId,
2030
- originalParent: child?.parent,
2031
- parentName: hierarchyData.name,
2032
- parentCategory: hierarchyData.primaryCategory
2033
- });
2034
2025
  }
2035
2026
  this.hashmap[child.identifier] = localMap;
2036
2027
  });
@@ -2162,11 +2153,6 @@ class AppTocService {
2162
2153
  console.warn('āš ļø Hashmap is empty - cannot compute milestone locking');
2163
2154
  return;
2164
2155
  }
2165
- console.log('\n═══════════════════════════════════════════════════════════');
2166
- console.log('šŸ”’ MILESTONE LOCKING COMPUTATION START');
2167
- console.log('═══════════════════════════════════════════════════════════');
2168
- console.log('User enrolled:', isEnrolled);
2169
- console.log('Hashmap size:', Object.keys(this.hashmap).length);
2170
2156
  // STEP 1: Find all milestones and sort by index
2171
2157
  const milestoneEntries = Object.keys(this.hashmap)
2172
2158
  .filter(key => {
@@ -2184,11 +2170,8 @@ class AppTocService {
2184
2170
  return numA - numB;
2185
2171
  });
2186
2172
  if (milestoneEntries.length === 0) {
2187
- console.log('ā„¹ļø No milestones found - skipping milestone locking');
2188
- console.log('═══════════════════════════════════════════════════════════\n');
2189
2173
  return;
2190
2174
  }
2191
- console.log(`\nšŸ“ Found ${milestoneEntries.length} milestones:`);
2192
2175
  milestoneEntries.forEach((id, i) => {
2193
2176
  console.log(` M${i + 1}: ${this.hashmap[id].name || id}`);
2194
2177
  });
@@ -2207,32 +2190,30 @@ class AppTocService {
2207
2190
  });
2208
2191
  this.hashmap = { ...this.hashmap };
2209
2192
  this.hashmapUpdated.next({ timestamp: Date.now(), hashmap: this.hashmap });
2210
- console.log('═══════════════════════════════════════════════════════════\n');
2211
2193
  return;
2212
2194
  }
2213
2195
  // STEP 3: Check pre-assessment completion
2214
2196
  const isPreAssessmentCompleted = this.checkPreAssessmentCompletion();
2215
- console.log(`\nšŸ“ Pre-assessment: ${isPreAssessmentCompleted ? 'āœ… COMPLETE' : 'āŒ INCOMPLETE'}`);
2197
+ console.log('šŸ” [MILESTONE LOCK] Pre-assessment completed:', isPreAssessmentCompleted ? 'āœ… YES' : 'āŒ NO');
2216
2198
  // STEP 4: Compute locking for each milestone
2217
- console.log('\nšŸ” Computing milestone locks:');
2218
- console.log('─────────────────────────────────────────────────────────────');
2219
2199
  milestoneEntries.forEach((milestoneId, index) => {
2220
2200
  const milestone = this.hashmap[milestoneId];
2221
2201
  if (!milestone)
2222
2202
  return;
2223
2203
  const milestoneNum = index + 1;
2224
- console.log(`\nšŸ“Œ Milestone ${milestoneNum} (${milestone.name || milestoneId}):`);
2225
2204
  // MILESTONE 1: Unlocks when pre-assessment complete
2226
2205
  if (index === 0) {
2227
2206
  const isLocked = !isPreAssessmentCompleted;
2228
2207
  this.hashmap[milestoneId].computedIsLocked = isLocked;
2229
2208
  this.hashmap[milestoneId].unlockMessage = isLocked ?
2230
2209
  'Complete the preliminary assessment to unlock this milestone' : '';
2231
- console.log(` ${isLocked ? 'šŸ”’ LOCKED' : 'šŸ”“ UNLOCKED'}`);
2232
- console.log(` Reason: Pre-assessment ${isPreAssessmentCompleted ? 'complete' : 'incomplete'}`);
2210
+ console.log(`šŸ“Œ [MILESTONE 1] ${milestone.name}:`, {
2211
+ isPreAssessmentCompleted,
2212
+ isLocked: isLocked ? 'šŸ”’ LOCKED' : 'šŸ”“ UNLOCKED',
2213
+ unlockMessage: this.hashmap[milestoneId].unlockMessage
2214
+ });
2233
2215
  // For unlocked M1, check assessment locking
2234
2216
  if (!isLocked) {
2235
- console.log(` āœ“ Checking assessment lock...`);
2236
2217
  this.computeAssessmentLockingInMilestone(milestoneId);
2237
2218
  }
2238
2219
  else {
@@ -2251,27 +2232,38 @@ class AppTocService {
2251
2232
  const prevStatus = Number(previousMilestone.status) || 0;
2252
2233
  const prevCompletedLeafNodes = Number(previousMilestone.completedLeafNodesCount) || 0;
2253
2234
  const prevLeafNodesCount = Number(previousMilestone.leafNodesCount) || 0;
2235
+ console.log(`šŸ” [MILESTONE ${milestoneNum}] Checking if previous M${index} is complete:`, {
2236
+ previousMilestoneName: previousMilestone.name,
2237
+ completionPercentage: prevCompletionPct,
2238
+ completionStatus: prevCompletionStatus,
2239
+ status: prevStatus,
2240
+ completedLeafNodes: prevCompletedLeafNodes,
2241
+ leafNodesCount: prevLeafNodesCount
2242
+ });
2254
2243
  let previousMilestoneComplete = prevCompletionPct >= 100 ||
2255
2244
  prevCompletionStatus === 2 ||
2256
2245
  prevStatus === 2 ||
2257
2246
  (prevLeafNodesCount > 0 && prevCompletedLeafNodes >= prevLeafNodesCount);
2247
+ console.log(` Primary check result: ${previousMilestoneComplete ? 'āœ… COMPLETE' : 'āŒ INCOMPLETE'}`);
2258
2248
  // Fallback: Check individual items
2259
2249
  if (!previousMilestoneComplete) {
2250
+ console.log(` Running fallback check (mandatory content + assessment)...`);
2260
2251
  const isPreviousMilestoneAssessmentComplete = this.checkMilestoneAssessmentComplete(previousMilestoneId);
2261
2252
  const isPreviousMilestoneMandatoryComplete = this.checkMilestoneMandatoryContentComplete(previousMilestoneId);
2262
2253
  previousMilestoneComplete = isPreviousMilestoneAssessmentComplete && isPreviousMilestoneMandatoryComplete;
2263
- console.log(` Fallback check: mandatory=${isPreviousMilestoneMandatoryComplete}, assessment=${isPreviousMilestoneAssessmentComplete}`);
2254
+ console.log(` Fallback check: mandatory=${isPreviousMilestoneMandatoryComplete}, assessment=${isPreviousMilestoneAssessmentComplete}, result=${previousMilestoneComplete}`);
2264
2255
  }
2265
2256
  const isLocked = !previousMilestoneComplete;
2266
2257
  this.hashmap[milestoneId].computedIsLocked = isLocked;
2267
2258
  this.hashmap[milestoneId].unlockMessage = isLocked ?
2268
2259
  `Complete all mandatory content and assessment in Milestone ${index} to unlock this milestone` : '';
2269
- console.log(` ${isLocked ? 'šŸ”’ LOCKED' : 'šŸ”“ UNLOCKED'}`);
2270
- console.log(` Reason: Previous milestone ${previousMilestoneComplete ? 'complete' : 'incomplete'}`);
2271
- console.log(` Previous M${index} progress: ${prevCompletionPct}%`);
2260
+ console.log(` šŸ“Š [MILESTONE ${milestoneNum}] ${milestone.name}:`, {
2261
+ previousMilestoneComplete: previousMilestoneComplete ? 'āœ… YES' : 'āŒ NO',
2262
+ isLocked: isLocked ? 'šŸ”’ LOCKED' : 'šŸ”“ UNLOCKED',
2263
+ unlockMessage: this.hashmap[milestoneId].unlockMessage
2264
+ });
2272
2265
  // For unlocked milestones, check assessment locking
2273
2266
  if (!isLocked) {
2274
- console.log(` āœ“ Checking assessment lock...`);
2275
2267
  this.computeAssessmentLockingInMilestone(milestoneId);
2276
2268
  }
2277
2269
  else {
@@ -2285,6 +2277,43 @@ class AppTocService {
2285
2277
  console.log(` šŸ”’ LOCKED (previous milestone not found)`);
2286
2278
  }
2287
2279
  });
2280
+ // STEP 4.5: Debug log milestone leaf nodes and their completion status
2281
+ console.log('\nšŸ“Š Milestone Leaf Nodes Completion Status:');
2282
+ milestoneEntries.forEach((milestoneId, index) => {
2283
+ const milestone = this.hashmap[milestoneId];
2284
+ if (!milestone)
2285
+ return;
2286
+ console.log(` Leaf Nodes Array:`, milestone.leafNodes);
2287
+ console.log(` Leaf Nodes Count: ${milestone.leafNodesCount}`);
2288
+ console.log(` Completed Leaf Nodes Count: ${milestone.completedLeafNodesCount || 0}`);
2289
+ console.log(` Completion %: ${milestone.completionPercentage}%`);
2290
+ if (milestone.leafNodes && milestone.leafNodes.length > 0) {
2291
+ console.log(` Individual Leaf Node Status:`);
2292
+ milestone.leafNodes.forEach((leafId, i) => {
2293
+ const leafData = this.hashmap[leafId];
2294
+ if (leafData) {
2295
+ const isComplete = leafData.completionStatus === 2 ||
2296
+ leafData.status === 2 ||
2297
+ (leafData.completionPercentage && leafData.completionPercentage >= 100) ||
2298
+ (leafData.progress && leafData.progress >= 100);
2299
+ console.log(` ${i + 1}. ${leafData.name || leafId} [${leafData.primaryCategory}]:`, {
2300
+ id: leafId,
2301
+ completionStatus: leafData.completionStatus,
2302
+ status: leafData.status,
2303
+ completionPercentage: leafData.completionPercentage,
2304
+ progress: leafData.progress,
2305
+ isComplete: isComplete ? 'āœ…' : 'āŒ'
2306
+ });
2307
+ }
2308
+ else {
2309
+ console.log(` ${i + 1}. āš ļø Leaf node ${leafId} NOT FOUND in hashmap`);
2310
+ }
2311
+ });
2312
+ }
2313
+ else {
2314
+ console.log(` āš ļø No leaf nodes defined for this milestone`);
2315
+ }
2316
+ });
2288
2317
  // STEP 5: Compute parent milestone lock status for ALL children
2289
2318
  console.log('\nšŸ”— Computing parent milestone locks for all children...');
2290
2319
  Object.keys(this.hashmap).forEach(key => {
@@ -2316,9 +2345,7 @@ class AppTocService {
2316
2345
  }
2317
2346
  });
2318
2347
  // STEP 6: Trigger hashmap update
2319
- console.log('\n─────────────────────────────────────────────────────────────');
2320
2348
  console.log('āœ… Milestone locking computation COMPLETE');
2321
- console.log('═══════════════════════════════════════════════════════════\n');
2322
2349
  this.hashmap = { ...this.hashmap };
2323
2350
  this.hashmapUpdated.next({ timestamp: Date.now(), hashmap: this.hashmap });
2324
2351
  }
@@ -2343,6 +2370,7 @@ class AppTocService {
2343
2370
  * Pre-assessment is typically a Course Assessment at the root level of Learning Pathway (before milestones)
2344
2371
  */
2345
2372
  checkPreAssessmentCompletion() {
2373
+ console.log('šŸ” [PRE-ASSESSMENT] Starting pre-assessment completion check');
2346
2374
  // Find the Learning Pathway root
2347
2375
  let learningPathwayId = null;
2348
2376
  for (const key of Object.keys(this.hashmap)) {
@@ -2352,7 +2380,7 @@ class AppTocService {
2352
2380
  break;
2353
2381
  }
2354
2382
  }
2355
- console.log('Checking pre-assessment completion. Learning Pathway ID:', learningPathwayId);
2383
+ console.log('šŸ” [PRE-ASSESSMENT] Learning Pathway ID:', learningPathwayId);
2356
2384
  // PRIORITY 1: Look for items explicitly marked as pre-assessment
2357
2385
  for (const key of Object.keys(this.hashmap)) {
2358
2386
  const item = this.hashmap[key];
@@ -2361,20 +2389,19 @@ class AppTocService {
2361
2389
  const isCompleted = (item.completionStatus === 2 || item.status === 2 ||
2362
2390
  (item.completionPercentage !== undefined && item.completionPercentage >= 100) ||
2363
2391
  (item.progress !== undefined && item.progress >= 100));
2364
- console.log(`āœ… Pre-assessment found (isPreAssessment flag): ${key}`, {
2365
- name: item.name,
2392
+ console.log(`āœ… [PRE-ASSESSMENT] Found (isPreAssessment flag): ${item.name}`, {
2393
+ id: key,
2366
2394
  completionStatus: item.completionStatus,
2367
2395
  status: item.status,
2368
2396
  completionPercentage: item.completionPercentage,
2369
2397
  progress: item.progress,
2370
- isCompleted,
2371
- // Show ALL fields to debug what's available
2372
- allData: JSON.stringify(item, null, 2)
2398
+ isCompleted: isCompleted ? 'āœ… COMPLETE' : 'āŒ INCOMPLETE'
2373
2399
  });
2374
2400
  // Pre-assessment exists - return its completion status (true/false)
2375
2401
  return isCompleted;
2376
2402
  }
2377
2403
  }
2404
+ console.log('šŸ” [PRE-ASSESSMENT] No item with isPreAssessment flag found, checking for first non-milestone child');
2378
2405
  // PRIORITY 2: Find pre-assessment in hashmap - direct child of Learning Pathway that is NOT a milestone
2379
2406
  // Check for first non-milestone child of Learning Pathway
2380
2407
  for (const key of Object.keys(this.hashmap)) {
@@ -2383,26 +2410,32 @@ class AppTocService {
2383
2410
  if (item.parent !== learningPathwayId)
2384
2411
  continue;
2385
2412
  // Skip milestones
2386
- if (item.isMilestone)
2413
+ if (item.isMilestone || item.primaryCategory === 'Milestone')
2387
2414
  continue;
2415
+ console.log(`šŸ” [PRE-ASSESSMENT] Checking potential pre-assessment: ${item.name}`, {
2416
+ id: key,
2417
+ parent: item.parent,
2418
+ primaryCategory: item.primaryCategory,
2419
+ courseCategory: item.courseCategory,
2420
+ isMilestone: item.isMilestone
2421
+ });
2388
2422
  // This is a non-milestone direct child - it's the pre-assessment
2389
2423
  // Must check for actual completion - not just any value
2390
2424
  const isCompleted = (item.completionStatus === 2 || item.status === 2 ||
2391
2425
  (item.completionPercentage !== undefined && item.completionPercentage >= 100) ||
2392
2426
  (item.progress !== undefined && item.progress >= 100));
2393
- console.log(`āœ… Pre-assessment found (first non-milestone child): ${key}`, {
2394
- name: item.name,
2427
+ console.log(`āœ… [PRE-ASSESSMENT] Found (first non-milestone child): ${item.name}`, {
2428
+ id: key,
2395
2429
  completionStatus: item.completionStatus,
2396
2430
  status: item.status,
2397
2431
  completionPercentage: item.completionPercentage,
2398
2432
  progress: item.progress,
2399
- isCompleted,
2400
- // Show ALL fields to debug what's available
2401
- allData: JSON.stringify(item, null, 2)
2433
+ isCompleted: isCompleted ? 'āœ… COMPLETE' : 'āŒ INCOMPLETE'
2402
2434
  });
2403
2435
  // Pre-assessment exists - return its completion status (true/false)
2404
2436
  return isCompleted;
2405
2437
  }
2438
+ console.log('šŸ” [PRE-ASSESSMENT] No pre-assessment found yet');
2406
2439
  // CRITICAL: If we reach here, no pre-assessment was found in the hashmap
2407
2440
  // This could mean either:
2408
2441
  // 1. There IS no pre-assessment (unlock M1 by default)
@@ -2411,41 +2444,60 @@ class AppTocService {
2411
2444
  const hasAnyChildren = Object.keys(this.hashmap).some(key => this.hashmap[key].parent === learningPathwayId);
2412
2445
  if (!hasAnyChildren) {
2413
2446
  // Hashmap not populated yet - lock M1 until data is ready
2414
- console.log('āš ļø No children found in hashmap yet - locking M1 until data is ready');
2447
+ console.log('āš ļø [PRE-ASSESSMENT] No children found in hashmap yet - LOCKING M1 until data is ready');
2415
2448
  return false;
2416
2449
  }
2417
2450
  // Hashmap is populated but no pre-assessment found - allow M1 to unlock
2418
- console.log('No pre-assessment found in fully populated hashmap - allowing M1 to be unlocked by default');
2451
+ console.log('ā„¹ļø [PRE-ASSESSMENT] No pre-assessment found in fully populated hashmap - allowing M1 to be unlocked by default');
2419
2452
  return true;
2420
2453
  }
2421
2454
  /**
2422
2455
  * Check if a milestone's assessment is completed
2423
- * Searches for any assessment type (Course Assessment, Final Assessment, etc.) that belongs to this milestone
2456
+ * ONLY checks the milestone assessment (Course Assessment that is a direct child of the milestone)
2457
+ * Does NOT check assessments nested inside courses within the milestone
2424
2458
  */
2425
2459
  checkMilestoneAssessmentComplete(milestoneId) {
2426
2460
  const milestone = this.hashmap[milestoneId];
2427
- // Check all items in hashmap that are assessments and belong to this milestone
2461
+ let foundMilestoneAssessment = false;
2462
+ let isMilestoneAssessmentComplete = false;
2463
+ console.log(`šŸ” Checking milestone assessment for: ${milestoneId}`);
2464
+ // Check all items in hashmap that are DIRECT children of this milestone
2428
2465
  for (const key of Object.keys(this.hashmap)) {
2429
2466
  const item = this.hashmap[key];
2430
- // Check if this is an assessment type
2431
- const isAssessmentType = item.primaryCategory === 'Course Assessment' ||
2467
+ // CRITICAL: Only check DIRECT children (parent === milestoneId)
2468
+ if (item.parent !== milestoneId)
2469
+ continue;
2470
+ // Check if this is a milestone assessment (Course Assessment as direct child of milestone)
2471
+ const isMilestoneAssessment = item.primaryCategory === 'Course Assessment' ||
2432
2472
  item.primaryCategory === 'Final Assessment' ||
2433
- item.primaryCategory === 'Practice Question Set' ||
2434
- item.courseCategory === 'Course Assessment' ||
2435
- item.courseCategory === 'Final Assessment' ||
2436
- (item.name && item.name.toLowerCase().includes('assessment'));
2437
- if (!isAssessmentType)
2473
+ item.primaryCategory === 'Standalone Assessment';
2474
+ if (!isMilestoneAssessment)
2438
2475
  continue;
2439
- // Check if this assessment belongs to this milestone
2440
- if (this.isChildOfMilestone(key, milestoneId)) {
2441
- const isCompleted = item.completionStatus === 2 || item.status === 2 || item.completionPercentage >= 100 || item.progress >= 100;
2442
- if (isCompleted) {
2443
- return true;
2444
- }
2476
+ // Found the milestone assessment
2477
+ foundMilestoneAssessment = true;
2478
+ const isCompleted = item.completionStatus === 2 || item.status === 2 || item.completionPercentage >= 100 || item.progress >= 100;
2479
+ console.log(`šŸ“‹ Found milestone assessment (direct child): ${item.name || key}`, {
2480
+ milestoneId,
2481
+ assessmentId: key,
2482
+ completionStatus: item.completionStatus,
2483
+ status: item.status,
2484
+ completionPercentage: item.completionPercentage,
2485
+ isCompleted
2486
+ });
2487
+ if (isCompleted) {
2488
+ isMilestoneAssessmentComplete = true;
2445
2489
  }
2490
+ // Usually only one milestone assessment per milestone, but check all just in case
2446
2491
  }
2447
- // If no assessment found, consider it as completed (no assessment requirement)
2448
- return true;
2492
+ // CRITICAL LOGIC:
2493
+ // - If NO milestone assessment found → consider complete (no assessment requirement)
2494
+ // - If milestone assessment found → it must be completed
2495
+ if (!foundMilestoneAssessment) {
2496
+ console.log(`ā„¹ļø No milestone assessment (direct child) found for ${milestoneId} - considering as complete`);
2497
+ return true;
2498
+ }
2499
+ console.log(`${isMilestoneAssessmentComplete ? 'āœ…' : 'āŒ'} Milestone ${milestoneId} assessment: ${isMilestoneAssessmentComplete ? 'COMPLETE' : 'INCOMPLETE'}`);
2500
+ return isMilestoneAssessmentComplete;
2449
2501
  }
2450
2502
  /**
2451
2503
  * Check if a milestone's mandatory content is completed
@@ -2455,6 +2507,7 @@ class AppTocService {
2455
2507
  const milestone = this.hashmap[milestoneId];
2456
2508
  let mandatoryCount = 0;
2457
2509
  let completedMandatoryCount = 0;
2510
+ console.log(`šŸ” Checking mandatory content for milestone: ${milestoneId}`);
2458
2511
  // Check all items in hashmap that are direct children of this milestone (courses)
2459
2512
  for (const key of Object.keys(this.hashmap)) {
2460
2513
  const item = this.hashmap[key];
@@ -2467,20 +2520,36 @@ class AppTocService {
2467
2520
  // Skip assessments - they're checked separately
2468
2521
  const isAssessment = item.primaryCategory === 'Course Assessment' ||
2469
2522
  item.primaryCategory === 'Final Assessment' ||
2523
+ item.primaryCategory === 'Standalone Assessment' ||
2470
2524
  item.courseCategory === 'Course Assessment' ||
2471
2525
  (item.name && item.name.toLowerCase().includes('assessment'));
2472
2526
  if (isAssessment)
2473
2527
  continue;
2474
- // Check if this content is mandatory
2475
- if (item.isMandatory) {
2528
+ // CRITICAL: Check if this content is mandatory
2529
+ // By default, courses ARE mandatory unless explicitly marked as optional (isMandatory: false)
2530
+ const isMandatory = item.isMandatory !== false;
2531
+ if (isMandatory) {
2476
2532
  mandatoryCount++;
2477
2533
  const isCompleted = item.completionStatus === 2 || item.status === 2 || item.completionPercentage >= 100 || item.progress >= 100;
2534
+ console.log(` šŸ“ Mandatory item: ${item.name || key}`, {
2535
+ isMandatory: item.isMandatory,
2536
+ completionStatus: item.completionStatus,
2537
+ status: item.status,
2538
+ completionPercentage: item.completionPercentage,
2539
+ progress: item.progress,
2540
+ isCompleted,
2541
+ hasCompletionStatus: item.completionStatus !== undefined,
2542
+ hasStatus: item.status !== undefined,
2543
+ hasCompletionPercentage: item.completionPercentage !== undefined,
2544
+ hasProgress: item.progress !== undefined
2545
+ });
2478
2546
  if (isCompleted) {
2479
2547
  completedMandatoryCount++;
2480
2548
  }
2481
2549
  }
2482
2550
  }
2483
2551
  const allComplete = mandatoryCount === 0 || completedMandatoryCount >= mandatoryCount;
2552
+ console.log(`šŸ“Š Milestone ${milestoneId} mandatory status: ${completedMandatoryCount}/${mandatoryCount} complete = ${allComplete ? 'āœ… ALL COMPLETE' : 'āŒ INCOMPLETE'}`);
2484
2553
  return allComplete;
2485
2554
  }
2486
2555
  /**
@@ -6318,6 +6387,7 @@ class AppTocContentCardV2Component {
6318
6387
  this._cachedIsMilestoneAssessment = false;
6319
6388
  this._cachedIsMilestoneAssessmentLocked = false;
6320
6389
  this._cachedResourceLink = { url: '', queryParams: {} };
6390
+ this._cachedMilestoneCompletedCount = 0;
6321
6391
  this._cacheInitialized = false;
6322
6392
  }
6323
6393
  ngOnInit() {
@@ -6331,7 +6401,6 @@ class AppTocContentCardV2Component {
6331
6401
  // Subscribe to hashmap updates to recompute cached properties when progress changes
6332
6402
  this.hashmapUpdatedSubscription = this.appTocSvc.hashmapUpdated$.subscribe((update) => {
6333
6403
  if (update && update.hashmap) {
6334
- console.log('šŸ”„ Hashmap updated, recomputing cached properties for:', this.content?.identifier);
6335
6404
  // IMPORTANT: Update hierarchyMapData with the latest hashmap from the service
6336
6405
  // This ensures the component uses the updated data, not the stale @Input reference
6337
6406
  this.hierarchyMapData = update.hashmap;
@@ -6385,6 +6454,7 @@ class AppTocContentCardV2Component {
6385
6454
  this._cachedIsMilestoneAssessment = this.computeIsMilestoneAssessment();
6386
6455
  this._cachedIsMilestoneAssessmentLocked = this.computeIsMilestoneAssessmentLocked();
6387
6456
  this._cachedResourceLink = this.computeResourceLink();
6457
+ this._cachedMilestoneCompletedCount = this.computeMilestoneCompletedCount();
6388
6458
  this._cacheInitialized = true;
6389
6459
  }
6390
6460
  computeIsResource() {
@@ -7189,6 +7259,12 @@ class AppTocContentCardV2Component {
7189
7259
  return this.computeIsMilestone();
7190
7260
  }
7191
7261
  getMilestoneCompletedCount() {
7262
+ if (this._cacheInitialized) {
7263
+ return this._cachedMilestoneCompletedCount;
7264
+ }
7265
+ return this.computeMilestoneCompletedCount();
7266
+ }
7267
+ computeMilestoneCompletedCount() {
7192
7268
  if (!this.content || !this.hierarchyMapData) {
7193
7269
  return 0;
7194
7270
  }
@@ -7199,8 +7275,15 @@ class AppTocContentCardV2Component {
7199
7275
  let completedCount = 0;
7200
7276
  milestoneData.leafNodes.forEach((leafId) => {
7201
7277
  const leafData = this.hierarchyMapData[leafId];
7202
- if (leafData && leafData.completionStatus === 2) {
7203
- completedCount++;
7278
+ if (leafData) {
7279
+ // CRITICAL: Check multiple completion indicators
7280
+ const isCompleted = leafData.completionStatus === 2 ||
7281
+ leafData.status === 2 ||
7282
+ (leafData.completionPercentage && leafData.completionPercentage >= 100) ||
7283
+ (leafData.progress && leafData.progress >= 100);
7284
+ if (isCompleted) {
7285
+ completedCount++;
7286
+ }
7204
7287
  }
7205
7288
  });
7206
7289
  return completedCount;
@@ -15567,7 +15650,7 @@ class AppTocV2Service {
15567
15650
  milestoneIndex++;
15568
15651
  });
15569
15652
  contentHeirarchy['leafNodes'] = [...leafNodes];
15570
- console.log('content Heirarchy', contentHeirarchy);
15653
+ contentHeirarchy['leafNodesCount'] = leafNodes.length;
15571
15654
  return contentHeirarchy;
15572
15655
  }
15573
15656
  mapModuleCount(content) {
@@ -15583,9 +15666,6 @@ class AppTocV2Service {
15583
15666
  }
15584
15667
  }
15585
15668
  mapContentHierarchyProgressUpdate(contentHeirarchyData, enrollmentListData) {
15586
- console.log('=== mapContentHierarchyProgressUpdate ===');
15587
- console.log('Content hierarchy:', contentHeirarchyData?.name, contentHeirarchyData?.identifier);
15588
- console.log('Enrollment list data:', enrollmentListData);
15589
15669
  if (contentHeirarchyData && contentHeirarchyData.children) {
15590
15670
  let totalLeafNodes = 0;
15591
15671
  let totalCompletedLeafNodes = 0;
@@ -15702,13 +15782,6 @@ class AppTocV2Service {
15702
15782
  }
15703
15783
  // Try both contentId and collectionId as the API response may use either field
15704
15784
  const nodeEnrollData = enrollment?.contentList?.find((ele) => ele?.contentId === node.identifier || ele?.collectionId === node.identifier);
15705
- console.log(`Updating node progress for ${node.identifier} (${node.name}):`, {
15706
- hasEnrollment: !!enrollment,
15707
- hasNodeEnrollData: !!nodeEnrollData,
15708
- nodeEnrollData,
15709
- currentCompletionStatus: node.completionStatus,
15710
- currentCompletionPercentage: node.completionPercentage
15711
- });
15712
15785
  if (enrollment && nodeEnrollData && nodeEnrollData.status < 2) {
15713
15786
  node.completionPercentage = nodeEnrollData.completionPercentage || nodeEnrollData.progress || 0;
15714
15787
  node.completionStatus = Number(nodeEnrollData.status) || 0;
@@ -20842,7 +20915,6 @@ class AppTocHomeV2Component {
20842
20915
  }
20843
20916
  }
20844
20917
  if (this.baseContentReadData?.courseCategory === 'Learning Pathway') {
20845
- console.log('User enrolled in LP - computing milestone locks with isEnrolled=true');
20846
20918
  this.tocSvc.callHirarchyProgressHashmap(this.content);
20847
20919
  this.tocSvc.computeMilestoneLockingStatus(true);
20848
20920
  this.syncMilestoneLockStatus();
@@ -20875,7 +20947,6 @@ class AppTocHomeV2Component {
20875
20947
  this.tocSvc.callHirarchyProgressHashmap(this.content);
20876
20948
  // For Learning Pathways, compute milestone locking when not enrolled (all locked)
20877
20949
  if (this.baseContentReadData?.courseCategory === 'Learning Pathway') {
20878
- console.log('User not enrolled - computing milestone locks');
20879
20950
  this.tocSvc.computeMilestoneLockingStatus(false);
20880
20951
  this.syncMilestoneLockStatus();
20881
20952
  }
@@ -21509,7 +21580,6 @@ class AppTocHomeV2Component {
21509
21580
  this.hashmapUpdatedSubscription = this.tocSvc.hashmapUpdated$.subscribe((update) => {
21510
21581
  if (update && this.baseContentReadData?.courseCategory === 'Learning Pathway') {
21511
21582
  const isEnrolled = this.userEnrollmentList && this.userEnrollmentList.length > 0;
21512
- console.log('Hashmap updated at:', update.timestamp, 'Enrolled:', isEnrolled, 'Syncing milestone lock status...');
21513
21583
  // Sync content tree's isLocked with the updated hashmap values
21514
21584
  this.syncMilestoneLockStatus();
21515
21585
  }
@@ -21633,18 +21703,17 @@ class AppTocHomeV2Component {
21633
21703
  if (this.baseContentReadData?.courseCategory === 'Learning Pathway') {
21634
21704
  return new Promise((resolve) => {
21635
21705
  const content = this.baseContentReadData;
21706
+ // STEP 1: Construct hierarchy structure
21636
21707
  this.content = this.appTocV2Svc.constructHeirarchyData(content);
21637
- // Update progress with latest enrollment data
21708
+ // STEP 2: Update progress with latest enrollment data
21638
21709
  this.appTocV2Svc.mapContentHierarchyProgressUpdate(this.content, this.userEnrollmentList);
21639
- // Create hashmap and compute milestone locking after progress is updated
21710
+ // STEP 3: Create hashmap from updated hierarchy (this should preserve completion status)
21640
21711
  this.tocSvc.callHirarchyProgressHashmap(this.content);
21641
- console.log('Learning Pathway hashmap updated:', this.tocSvc.hashmap);
21642
21712
  // Check if user is enrolled
21643
21713
  const isEnrolled = this.userEnrollmentList && this.userEnrollmentList.length > 0;
21644
- // Compute milestone locking status with enrollment status and updated progress data
21714
+ // STEP 4: Compute milestone locking status with enrollment status and updated progress data
21645
21715
  this.tocSvc.computeMilestoneLockingStatus(isEnrolled);
21646
- console.log('Milestone locking recomputed. Enrolled:', isEnrolled);
21647
- // Sync content tree's isLocked with computed values
21716
+ // STEP 5: Sync content tree's isLocked with computed values
21648
21717
  this.syncMilestoneLockStatus();
21649
21718
  this.getOrgIdForShare();
21650
21719
  this.getTocStructure();
@@ -21728,7 +21797,6 @@ class AppTocHomeV2Component {
21728
21797
  if (oldLocked !== child.isLocked) {
21729
21798
  hasChanges = true;
21730
21799
  }
21731
- console.log(`Synced lock status for ${child.name} (${child.identifier}): isLocked=${child.isLocked}`);
21732
21800
  }
21733
21801
  }
21734
21802
  });
@@ -21743,7 +21811,6 @@ class AppTocHomeV2Component {
21743
21811
  return;
21744
21812
  }
21745
21813
  try {
21746
- console.log('šŸ”„ Refreshing milestone locks from hashmap...');
21747
21814
  // Check if user is enrolled
21748
21815
  const isEnrolled = this.userEnrollmentList && this.userEnrollmentList.length > 0;
21749
21816
  // Recompute milestone locking status from existing hashmap