@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.
- package/esm2022/lib/_collection/_common/content-toc/app-toc-content-card-v2/app-toc-content-card-v2.component.mjs +18 -4
- package/esm2022/lib/_services/widget-content.service.mjs +3 -3
- package/esm2022/lib/components/app-toc-home-v2/app-toc-home-v2.component.mjs +6 -12
- package/esm2022/lib/services/app-toc-v2.service.mjs +2 -12
- package/esm2022/lib/services/app-toc.service.mjs +135 -66
- package/fesm2022/sunbird-cb-toc.mjs +159 -92
- package/fesm2022/sunbird-cb-toc.mjs.map +1 -1
- package/lib/_collection/_common/content-toc/app-toc-content-card-v2/app-toc-content-card-v2.component.d.ts +2 -0
- package/lib/_collection/_common/content-toc/app-toc-content-card-v2/app-toc-content-card-v2.component.d.ts.map +1 -1
- package/lib/components/app-toc-home-v2/app-toc-home-v2.component.d.ts.map +1 -1
- package/lib/services/app-toc-v2.service.d.ts.map +1 -1
- package/lib/services/app-toc.service.d.ts +2 -1
- package/lib/services/app-toc.service.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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/
|
|
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/
|
|
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(
|
|
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(
|
|
2232
|
-
|
|
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(` ${
|
|
2270
|
-
|
|
2271
|
-
|
|
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('
|
|
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(`ā
|
|
2365
|
-
|
|
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(`ā
|
|
2394
|
-
|
|
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 -
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
//
|
|
2431
|
-
|
|
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 === '
|
|
2434
|
-
|
|
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
|
-
//
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
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
|
-
//
|
|
2448
|
-
|
|
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
|
-
|
|
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
|
|
7203
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|