elearning/Backend/src/validators/ChaptersLesson.validator.ts
JakkrapartXD b56f604890
All checks were successful
Build and Deploy Backend / Build Backend Docker Image (push) Successful in 26s
Build and Deploy Backend / Deploy E-learning Backend to Dev Server (push) Successful in 3s
Build and Deploy Backend / Notify Deployment Status (push) Successful in 2s
feat: introduce Joi validation schemas and integrate them across various controllers for categories, lessons, courses, chapters, announcements, and admin course approvals.
2026-02-18 15:59:40 +07:00

186 lines
5.7 KiB
TypeScript

import Joi from 'joi';
// Multi-language validation schema
const multiLangSchema = Joi.object({
th: Joi.string().required().messages({
'any.required': 'Thai text is required'
}),
en: Joi.string().required().messages({
'any.required': 'English text is required'
})
}).required();
const multiLangOptionalSchema = Joi.object({
th: Joi.string().optional(),
en: Joi.string().optional()
}).optional();
// ============================================
// Chapter Validators
// ============================================
/**
* Validator for creating a chapter
*/
export const CreateChapterValidator = Joi.object({
title: multiLangSchema.messages({
'any.required': 'Title is required'
}),
description: multiLangOptionalSchema,
sort_order: Joi.number().integer().min(0).optional()
});
/**
* Validator for updating a chapter
*/
export const UpdateChapterValidator = Joi.object({
title: multiLangOptionalSchema,
description: multiLangOptionalSchema,
sort_order: Joi.number().integer().min(0).optional(),
is_published: Joi.boolean().optional()
});
/**
* Validator for reordering a chapter
*/
export const ReorderChapterValidator = Joi.object({
sort_order: Joi.number().integer().min(0).required().messages({
'any.required': 'Sort order is required',
'number.min': 'Sort order must be at least 0'
})
});
// ============================================
// Lesson Validators
// ============================================
/**
* Validator for creating a lesson
*/
export const CreateLessonValidator = Joi.object({
title: multiLangSchema.messages({
'any.required': 'Title is required'
}),
content: multiLangOptionalSchema,
type: Joi.string().valid('VIDEO', 'QUIZ').required().messages({
'any.only': 'Type must be either VIDEO or QUIZ',
'any.required': 'Type is required'
}),
sort_order: Joi.number().integer().min(0).optional()
});
/**
* Validator for updating a lesson
*/
export const UpdateLessonValidator = Joi.object({
title: multiLangOptionalSchema,
content: multiLangOptionalSchema,
duration_minutes: Joi.number().min(0).optional().messages({
'number.min': 'Duration must be at least 0'
}),
sort_order: Joi.number().integer().min(0).optional(),
prerequisite_lesson_ids: Joi.array().items(Joi.number().integer().positive()).optional(),
is_published: Joi.boolean().optional()
});
/**
* Validator for reordering lessons
*/
export const ReorderLessonsValidator = Joi.object({
lesson_id: Joi.number().integer().positive().required().messages({
'any.required': 'Lesson ID is required'
}),
sort_order: Joi.number().integer().min(0).required().messages({
'any.required': 'Sort order is required'
})
});
// ============================================
// Quiz Question Validators
// ============================================
/**
* Validator for quiz choice
*/
const QuizChoiceValidator = Joi.object({
text: multiLangSchema.messages({
'any.required': 'Choice text is required'
}),
is_correct: Joi.boolean().required().messages({
'any.required': 'is_correct is required'
}),
sort_order: Joi.number().integer().min(0).optional()
});
/**
* Validator for adding a question to a quiz
*/
export const AddQuestionValidator = Joi.object({
question: multiLangSchema.messages({
'any.required': 'Question is required'
}),
explanation: multiLangOptionalSchema,
question_type: Joi.string()
.valid('MULTIPLE_CHOICE', 'TRUE_FALSE', 'SHORT_ANSWER')
.required()
.messages({
'any.only': 'Question type must be MULTIPLE_CHOICE, TRUE_FALSE, or SHORT_ANSWER',
'any.required': 'Question type is required'
}),
sort_order: Joi.number().integer().min(0).optional(),
choices: Joi.array().items(QuizChoiceValidator).min(1).optional().messages({
'array.min': 'At least one choice is required for multiple choice questions'
})
});
/**
* Validator for updating a question
*/
export const UpdateQuestionValidator = Joi.object({
question: multiLangOptionalSchema,
explanation: multiLangOptionalSchema,
question_type: Joi.string()
.valid('MULTIPLE_CHOICE', 'TRUE_FALSE', 'SHORT_ANSWER')
.optional()
.messages({
'any.only': 'Question type must be MULTIPLE_CHOICE, TRUE_FALSE, or SHORT_ANSWER'
}),
sort_order: Joi.number().integer().min(0).optional(),
choices: Joi.array().items(QuizChoiceValidator).min(1).optional().messages({
'array.min': 'At least one choice is required'
})
});
/**
* Validator for reordering a question
*/
export const ReorderQuestionValidator = Joi.object({
sort_order: Joi.number().integer().min(0).required().messages({
'any.required': 'Sort order is required',
'number.min': 'Sort order must be at least 0'
})
});
// ============================================
// Quiz Settings Validator
// ============================================
/**
* Validator for updating quiz settings
*/
export const UpdateQuizValidator = Joi.object({
title: multiLangOptionalSchema,
description: multiLangOptionalSchema,
passing_score: Joi.number().min(0).max(100).optional().messages({
'number.min': 'Passing score must be at least 0',
'number.max': 'Passing score must not exceed 100'
}),
time_limit: Joi.number().min(0).optional().messages({
'number.min': 'Time limit must be at least 0'
}),
shuffle_questions: Joi.boolean().optional(),
shuffle_choices: Joi.boolean().optional(),
show_answers_after_completion: Joi.boolean().optional(),
is_skippable: Joi.boolean().optional(),
allow_multiple_attempts: Joi.boolean().optional()
});