10 KiB
10 KiB
API Usage Examples - Lesson Prerequisites
New Endpoints Usage Examples
1. Check Lesson Access (Student)
GET /students/courses/:courseId/lessons/:lessonId/access-check
Purpose: ตรวจสอบว่าสามารถเข้าถึง lesson ได้หรือไม่ โดยไม่ต้อง load content
Request:
GET /api/students/courses/1/lessons/3/access-check
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 200 (Can Access):
{
"can_access": true,
"lesson": {
"id": 3,
"title": "แบบทดสอบท้ายบท",
"type": "quiz",
"sort_order": 3
}
}
Response 200 (Locked - Incomplete Prerequisites):
{
"can_access": false,
"reason": "incomplete_prerequisites",
"message": "กรุณาเรียนบทเรียนก่อนหน้าให้เสร็จก่อน",
"required_lessons": [
{
"id": 1,
"title": "บทที่ 1: แนะนำ",
"type": "video",
"is_completed": true
},
{
"id": 2,
"title": "บทที่ 2: ตัวแปร",
"type": "video",
"is_completed": false
}
],
"missing_lessons": [2],
"next_available_lesson": {
"id": 2,
"title": "บทที่ 2: ตัวแปร"
}
}
Response 200 (Locked - Quiz Not Passed):
{
"can_access": false,
"reason": "quiz_not_passed",
"message": "กรุณาทำแบบทดสอบให้ผ่านก่อน",
"required_quiz": {
"lesson_id": 3,
"title": "แบบทดสอบท้ายบท",
"passing_score": 70,
"your_best_score": 55,
"attempts_used": 2,
"max_attempts": 3
}
}
2. Get Learning Page with Lock Status (Student)
GET /students/courses/:courseId/learn
Changes: เพิ่ม is_locked และ lock_reason ในแต่ละ lesson
Request:
GET /api/students/courses/1/learn
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 200:
{
"course": {
"id": 1,
"title": "Python สำหรับผู้เริ่มต้น"
},
"enrollment": {
"status": "enrolled",
"progress_percentage": 35
},
"chapters": [
{
"id": 1,
"title": "บทที่ 1: พื้นฐาน",
"sort_order": 1,
"lessons": [
{
"id": 1,
"title": "1.1 แนะนำ Python",
"type": "video",
"duration": "00:15:30",
"is_completed": true,
"is_locked": false,
"completed_at": "2024-12-23T10:00:00Z"
},
{
"id": 2,
"title": "1.2 ตัวแปร",
"type": "video",
"duration": "00:20:00",
"is_completed": false,
"is_locked": false
},
{
"id": 3,
"title": "1.3 แบบทดสอบ",
"type": "quiz",
"is_completed": false,
"is_locked": true,
"lock_reason": "incomplete_prerequisites",
"required_lessons": [1, 2]
},
{
"id": 4,
"title": "1.4 ขั้นสูง",
"type": "video",
"duration": "00:25:00",
"is_completed": false,
"is_locked": true,
"lock_reason": "quiz_not_passed",
"required_quiz": 3
}
]
}
]
}
3. Get Lesson Content with Prerequisites Check (Student)
GET /students/courses/:courseId/lessons/:lessonId
Changes: ตรวจสอบ prerequisites ก่อน return content
Request:
GET /api/students/courses/1/lessons/3
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 403 (Locked):
{
"error": {
"code": "LESSON_LOCKED",
"message": "Cannot access this lesson",
"reason": "incomplete_prerequisites",
"required_lessons": [1, 2],
"completed_lessons": [1],
"missing_lessons": [2],
"next_available_lesson": {
"id": 2,
"title": "บทที่ 2: ตัวแปร",
"type": "video"
}
}
}
4. Create Lesson with Prerequisites (Instructor)
POST /instructor/courses/:courseId/chapters/:chapterId/lessons
Changes: เพิ่ม fields สำหรับ prerequisites
Request:
POST /api/instructor/courses/1/chapters/1/lessons
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"title": {
"th": "แบบทดสอบท้ายบท",
"en": "Chapter Quiz"
},
"content": {
"th": "<p>ทดสอบความเข้าใจ</p>",
"en": "<p>Test your understanding</p>"
},
"type": "quiz",
"sort_order": 3,
"is_sequential": true,
"prerequisite_lesson_ids": [1, 2],
"require_pass_quiz": true
}
Response 201:
{
"id": 3,
"chapter_id": 1,
"title": {
"th": "แบบทดสอบท้ายบท",
"en": "Chapter Quiz"
},
"type": "quiz",
"sort_order": 3,
"is_sequential": true,
"prerequisite_lesson_ids": [1, 2],
"require_pass_quiz": true,
"created_at": "2024-12-23T14:00:00Z"
}
Response 422 (Validation Error):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid prerequisites",
"details": [
{
"field": "prerequisite_lesson_ids",
"message": "Lesson ID 99 does not exist"
}
]
}
}
5. View Lesson Prerequisites (Instructor)
GET /instructor/courses/:courseId/lessons/:lessonId/prerequisites
Request:
GET /api/instructor/courses/1/lessons/4/prerequisites
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 200:
{
"lesson": {
"id": 4,
"title": "บทที่ 3: ขั้นสูง",
"type": "video",
"sort_order": 4
},
"is_sequential": true,
"prerequisites": [
{
"id": 3,
"title": "แบบทดสอบท้ายบท",
"type": "quiz",
"require_pass": true,
"passing_score": 70
}
],
"affected_students": {
"total_enrolled": 150,
"can_access": 85,
"locked": 65,
"breakdown": {
"incomplete_prerequisites": 45,
"quiz_not_passed": 20
}
}
}
6. Update Lesson Prerequisites (Instructor)
POST /instructor/courses/:courseId/lessons/:lessonId/prerequisites
Request:
POST /api/instructor/courses/1/lessons/4/prerequisites
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"is_sequential": true,
"prerequisite_lesson_ids": [3],
"require_pass_quiz": false
}
Response 200:
{
"lesson_id": 4,
"is_sequential": true,
"prerequisites": [
{
"id": 3,
"title": "แบบทดสอบท้ายบท",
"type": "quiz",
"require_pass": false
}
],
"message": "Prerequisites updated successfully",
"affected_students": {
"newly_unlocked": 20,
"still_locked": 45
}
}
Error Codes
New Error Codes for Prerequisites
| Code | Description |
|---|---|
LESSON_LOCKED |
Cannot access lesson due to prerequisites |
INCOMPLETE_PREREQUISITES |
Required lessons not completed |
QUIZ_NOT_PASSED |
Required quiz not passed |
INVALID_PREREQUISITES |
Invalid prerequisite configuration |
CIRCULAR_DEPENDENCY |
Circular dependency detected |
Frontend Integration Examples
1. Display Locked Lesson
function LessonItem({ lesson }) {
if (lesson.is_locked) {
return (
<div className="lesson-item locked">
<LockIcon />
<span className="lesson-title">{lesson.title}</span>
{lesson.lock_reason === 'incomplete_prerequisites' && (
<Tooltip>
กรุณาเรียนบทเรียนก่อนหน้าให้เสร็จก่อน
<ul>
{lesson.required_lessons.map(id => (
<li key={id}>Lesson {id}</li>
))}
</ul>
</Tooltip>
)}
{lesson.lock_reason === 'quiz_not_passed' && (
<Tooltip>
กรุณาทำแบบทดสอบให้ผ่านก่อน (ต้องได้ 70% ขึ้นไป)
</Tooltip>
)}
</div>
);
}
return (
<Link to={`/lessons/${lesson.id}`} className="lesson-item">
{lesson.is_completed ? <CheckIcon /> : <PlayIcon />}
<span>{lesson.title}</span>
</Link>
);
}
2. Check Access Before Navigation
async function navigateToLesson(courseId, lessonId) {
try {
const response = await fetch(
`/api/students/courses/${courseId}/lessons/${lessonId}/access-check`,
{
headers: {
'Authorization': `Bearer ${token}`
}
}
);
const data = await response.json();
if (data.can_access) {
// Navigate to lesson
router.push(`/courses/${courseId}/lessons/${lessonId}`);
} else {
// Show lock message
if (data.reason === 'incomplete_prerequisites') {
showAlert(`กรุณาเรียนบทเรียนก่อนหน้าให้เสร็จก่อน`);
// Highlight next available lesson
highlightLesson(data.next_available_lesson.id);
} else if (data.reason === 'quiz_not_passed') {
showAlert(`กรุณาทำแบบทดสอบให้ผ่านก่อน`);
// Navigate to quiz
router.push(`/courses/${courseId}/lessons/${data.required_quiz.lesson_id}`);
}
}
} catch (error) {
console.error('Error checking lesson access:', error);
}
}
Summary
New Endpoints: 3
GET /students/courses/:courseId/lessons/:lessonId/access-checkGET /instructor/courses/:courseId/lessons/:lessonId/prerequisitesPOST /instructor/courses/:courseId/lessons/:lessonId/prerequisites
Modified Endpoints: 4
GET /students/courses/:courseId/learn- เพิ่ม lock statusGET /students/courses/:courseId/lessons/:lessonId- เช็ค prerequisitesPOST /instructor/courses/:courseId/chapters/:chapterId/lessons- รองรับ prerequisitesPUT /instructor/courses/:courseId/lessons/:lessonId- รองรับ prerequisites