feat: normalize lesson sort_order to prevent gaps and duplicates in chapter lessons
Add normalizeLessonSortOrder method to ensure sequential 1-based sort_order values. Update reorderLesson to normalize before reordering and validate new position against lesson count. Call normalization after lesson deletion to fill gaps in remaining lessons.
This commit is contained in:
parent
69e4806831
commit
2b248cad10
1 changed files with 52 additions and 6 deletions
|
|
@ -483,10 +483,22 @@ export class ChaptersLessonService {
|
|||
if (currentLesson.chapter_id !== chapter_id) {
|
||||
throw new NotFoundError('Lesson not found in this chapter');
|
||||
}
|
||||
const oldSortOrder = currentLesson.sort_order;
|
||||
|
||||
// First, normalize sort_order to fix any gaps or duplicates
|
||||
await this.normalizeLessonSortOrder(chapter_id);
|
||||
|
||||
// Re-fetch the lesson to get updated sort_order after normalization
|
||||
const normalizedLesson = await prisma.lesson.findUnique({ where: { id: lesson_id } });
|
||||
if (!normalizedLesson) throw new NotFoundError('Lesson not found');
|
||||
|
||||
const oldSortOrder = normalizedLesson.sort_order;
|
||||
|
||||
// Get total lesson count to validate sort_order (1-based)
|
||||
const lessonCount = await prisma.lesson.count({ where: { chapter_id } });
|
||||
const validNewSortOrder = Math.max(1, Math.min(newSortOrder, lessonCount));
|
||||
|
||||
// If same position, no need to reorder
|
||||
if (oldSortOrder === newSortOrder) {
|
||||
if (oldSortOrder === validNewSortOrder) {
|
||||
const lessons = await prisma.lesson.findMany({
|
||||
where: { chapter_id },
|
||||
orderBy: { sort_order: 'asc' }
|
||||
|
|
@ -495,12 +507,12 @@ export class ChaptersLessonService {
|
|||
}
|
||||
|
||||
// Shift other lessons to make room for the insert
|
||||
if (oldSortOrder > newSortOrder) {
|
||||
if (oldSortOrder > validNewSortOrder) {
|
||||
// Moving up: shift lessons between newSortOrder and oldSortOrder-1 down by 1
|
||||
await prisma.lesson.updateMany({
|
||||
where: {
|
||||
chapter_id,
|
||||
sort_order: { gte: newSortOrder, lt: oldSortOrder }
|
||||
sort_order: { gte: validNewSortOrder, lt: oldSortOrder }
|
||||
},
|
||||
data: { sort_order: { increment: 1 } }
|
||||
});
|
||||
|
|
@ -509,14 +521,14 @@ export class ChaptersLessonService {
|
|||
await prisma.lesson.updateMany({
|
||||
where: {
|
||||
chapter_id,
|
||||
sort_order: { gt: oldSortOrder, lte: newSortOrder }
|
||||
sort_order: { gt: oldSortOrder, lte: validNewSortOrder }
|
||||
},
|
||||
data: { sort_order: { decrement: 1 } }
|
||||
});
|
||||
}
|
||||
|
||||
// Update the target lesson to the new position
|
||||
await prisma.lesson.update({ where: { id: lesson_id }, data: { sort_order: newSortOrder } });
|
||||
await prisma.lesson.update({ where: { id: lesson_id }, data: { sort_order: validNewSortOrder } })
|
||||
|
||||
// Fetch all lessons with updated order
|
||||
const lessons = await prisma.lesson.findMany({
|
||||
|
|
@ -576,10 +588,16 @@ export class ChaptersLessonService {
|
|||
}
|
||||
}
|
||||
|
||||
// Get chapter_id before deletion for normalization
|
||||
const chapterId = lesson.chapter_id;
|
||||
|
||||
// Delete lesson (CASCADE will delete: attachments, quiz, questions, choices)
|
||||
// Based on Prisma schema: onDelete: Cascade
|
||||
await prisma.lesson.delete({ where: { id: lesson_id } });
|
||||
|
||||
// Normalize sort_order for remaining lessons (fill gaps)
|
||||
await this.normalizeLessonSortOrder(chapterId);
|
||||
|
||||
return { code: 200, message: 'Lesson deleted successfully' };
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting lesson: ${error}`);
|
||||
|
|
@ -1219,6 +1237,34 @@ export class ChaptersLessonService {
|
|||
logger.info(`Normalized sort_order for quiz ${quizId}: ${questions.length} questions`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize sort_order for all lessons in a chapter
|
||||
* Ensures sort_order is sequential starting from 1 with no gaps or duplicates
|
||||
*
|
||||
* @param chapterId Chapter ID to normalize
|
||||
*/
|
||||
private async normalizeLessonSortOrder(chapterId: number): Promise<void> {
|
||||
// Get all lessons ordered by current sort_order
|
||||
const lessons = await prisma.lesson.findMany({
|
||||
where: { chapter_id: chapterId },
|
||||
orderBy: { sort_order: 'asc' },
|
||||
select: { id: true, sort_order: true }
|
||||
});
|
||||
|
||||
if (lessons.length === 0) return;
|
||||
|
||||
// Update each lesson with sequential sort_order starting from 1
|
||||
const updates = lessons.map((lesson, index) =>
|
||||
prisma.lesson.update({
|
||||
where: { id: lesson.id },
|
||||
data: { sort_order: index + 1 }
|
||||
})
|
||||
);
|
||||
|
||||
await prisma.$transaction(updates);
|
||||
logger.info(`Normalized sort_order for chapter ${chapterId}: ${lessons.length} lessons`);
|
||||
}
|
||||
|
||||
/**
|
||||
* อัพเดตการตั้งค่า Quiz
|
||||
* Update quiz settings (title, passing_score, time_limit, etc.)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue