CognxSafeTrack commited on
Commit ·
a66a580
1
Parent(s): d8ac16e
fix: re-validate COMPLETED→PENDING for text exercise responses + guard businessProfile.upsert at Day 10+
Browse files
apps/api/src/services/whatsapp.ts
CHANGED
|
@@ -459,6 +459,23 @@ export class WhatsAppService {
|
|
| 459 |
return;
|
| 460 |
}
|
| 461 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
const pendingProgress = await prisma.userProgress.findFirst({
|
| 463 |
where: { userId: user.id, exerciseStatus: { in: ['PENDING', 'PENDING_REMEDIATION', 'PENDING_DEEPDIVE'] }, trackId: activeEnrollment.trackId },
|
| 464 |
});
|
|
|
|
| 459 |
return;
|
| 460 |
}
|
| 461 |
|
| 462 |
+
// 🚨 TEXT RE-VALIDATION: Mirror the Worker-side `shouldForceRevalidation` logic.
|
| 463 |
+
// Image/Audio flows reset PENDING via WhatsAppLogic. For text, we do it here.
|
| 464 |
+
const userProgressState = await prisma.userProgress.findUnique({
|
| 465 |
+
where: { userId_trackId: { userId: user.id, trackId: activeEnrollment.trackId } }
|
| 466 |
+
});
|
| 467 |
+
const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000);
|
| 468 |
+
const isRecentlyCompleted = userProgressState?.exerciseStatus === 'COMPLETED'
|
| 469 |
+
&& userProgressState.updatedAt > tenMinutesAgo;
|
| 470 |
+
|
| 471 |
+
if (isRecentlyCompleted && !audioUrl && !imageUrl && text.length > 5 && !isSystemCommand) {
|
| 472 |
+
console.log(`[TXT-FLOW] 🔄 Re-validation User ${user.id} Day ${activeEnrollment.currentDay} (COMPLETED → PENDING)`);
|
| 473 |
+
await prisma.userProgress.update({
|
| 474 |
+
where: { id: userProgressState!.id },
|
| 475 |
+
data: { exerciseStatus: 'PENDING' }
|
| 476 |
+
});
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
const pendingProgress = await prisma.userProgress.findFirst({
|
| 480 |
where: { userId: user.id, exerciseStatus: { in: ['PENDING', 'PENDING_REMEDIATION', 'PENDING_DEEPDIVE'] }, trackId: activeEnrollment.trackId },
|
| 481 |
});
|
apps/whatsapp-worker/src/index.ts
CHANGED
|
@@ -227,11 +227,15 @@ const worker = new Worker('whatsapp-queue', async (job: Job) => {
|
|
| 227 |
updatePayload.teamMembers = [...existingTeam, ...(feedbackData as any).teamMembers];
|
| 228 |
}
|
| 229 |
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
}
|
| 236 |
} else {
|
| 237 |
// Success! Award Badges & Mark Completed (or keep Pending for Deep Dive)
|
|
@@ -275,11 +279,15 @@ const worker = new Worker('whatsapp-queue', async (job: Job) => {
|
|
| 275 |
updatePayload.teamMembers = [...existingTeam, ...(feedbackData as any).teamMembers];
|
| 276 |
}
|
| 277 |
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
}
|
| 284 |
|
| 285 |
// If we were in a remediation day (fractional) -> move to next integer day
|
|
|
|
| 227 |
updatePayload.teamMembers = [...existingTeam, ...(feedbackData as any).teamMembers];
|
| 228 |
}
|
| 229 |
|
| 230 |
+
try {
|
| 231 |
+
await (prisma as any).businessProfile.upsert({
|
| 232 |
+
where: { userId },
|
| 233 |
+
update: updatePayload,
|
| 234 |
+
create: { userId, ...updatePayload }
|
| 235 |
+
});
|
| 236 |
+
} catch (bpErr: unknown) {
|
| 237 |
+
console.error('[WORKER] BusinessProfile upsert failed (non-fatal, REMEDIATION path):', (bpErr as Error).message);
|
| 238 |
+
}
|
| 239 |
}
|
| 240 |
} else {
|
| 241 |
// Success! Award Badges & Mark Completed (or keep Pending for Deep Dive)
|
|
|
|
| 279 |
updatePayload.teamMembers = [...existingTeam, ...(feedbackData as any).teamMembers];
|
| 280 |
}
|
| 281 |
|
| 282 |
+
try {
|
| 283 |
+
await (prisma as any).businessProfile.upsert({
|
| 284 |
+
where: { userId },
|
| 285 |
+
update: updatePayload,
|
| 286 |
+
create: { userId, ...updatePayload }
|
| 287 |
+
});
|
| 288 |
+
} catch (bpErr: unknown) {
|
| 289 |
+
console.error('[WORKER] BusinessProfile upsert failed (non-fatal, SUCCESS path):', (bpErr as Error).message);
|
| 290 |
+
}
|
| 291 |
}
|
| 292 |
|
| 293 |
// If we were in a remediation day (fractional) -> move to next integer day
|