feat: implement core e-learning classroom functionality including login, lesson viewing, and quiz pages.

This commit is contained in:
supalerk-ar66 2026-01-29 16:26:30 +07:00
parent 0641b2547a
commit 8e57cb124a
3 changed files with 21 additions and 23 deletions

View file

@ -100,13 +100,14 @@ const handleLogin = async () => {
}
// Show error on specific fields
if (result.error === 'ไม่พบอีเมลในระบบ') {
errors.value.email = 'กรุณาเช็ค Email หรือ รหัสผ่านใหม่อีกครั้ง'
errors.value.password = 'กรุณาเช็ค Email หรือ รหัสผ่านใหม่อีกครั้ง'
} else if (result.error === 'Email ไม่ถูกต้อง') {
errors.value.email = result.error
// Show generic error for security (or specific if role mismatch)
if (result.error === 'Email ไม่ถูกต้อง') {
errors.value.email = result.error // Role mismatch case
} else {
errors.value.password = 'กรอกรหัสผ่านไม่ถูกต้อง'
// Generic login failure (401, 404, etc.)
const msg = 'กรุณาเช็ค Email หรือ รหัสผ่านใหม่อีกครั้ง'
errors.value.email = msg
errors.value.password = msg
}
}

View file

@ -282,8 +282,8 @@ const markLessonAsCompletedLocally = (lessonId: number) => {
for (const chapter of courseData.value.chapters) {
const lesson = chapter.lessons.find((l: any) => l.id === lessonId)
if (lesson) {
if (!lesson.progress) lesson.progress = {}
lesson.progress.is_completed = true
// Compatible with API structure
lesson.is_completed = true
break
}
}
@ -423,19 +423,9 @@ const onVideoEnded = async () => {
await loadCourseData()
alert(t('course.completed') || "ยินดีด้วย! คุณเรียนจบหลักสูตรแล้ว")
} else if (res.data.next_lesson_id) {
// Suggest next lesson
if (confirm(t('common.next') + '?')) {
const nextId = res.data.next_lesson_id
// Update URL without reload if possible, but router.push is standard
await router.push({
path: '/classroom/learning',
query: { ...route.query, lesson_id: nextId }
})
// Manually load the lesson since we don't have a watcher on query
handleLessonSelect(nextId)
}
// Auto-advance removed as per request.
// User will manually select the next lesson from the sidebar.
console.log('Video finished. Next lesson available:', res.data.next_lesson_id)
}
}
}
@ -532,7 +522,7 @@ onBeforeUnmount(() => {
</q-item-section>
<q-item-section side>
<q-icon v-if="lesson.progress?.is_completed" name="check_circle" color="positive" size="xs" />
<q-icon v-if="lesson.is_completed" name="check_circle" color="positive" size="xs" />
<q-icon v-else-if="currentLesson?.id === lesson.id" name="play_circle" color="primary" size="xs" />
<q-icon v-else name="radio_button_unchecked" color="grey-4" size="xs" />
</q-item-section>

View file

@ -15,7 +15,7 @@ const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const $q = useQuasar()
const { fetchCourseLearningInfo, fetchLessonContent, submitQuiz: apiSubmitQuiz } = useCourse()
const { fetchCourseLearningInfo, fetchLessonContent, submitQuiz: apiSubmitQuiz, markLessonComplete } = useCourse()
// State Management
const currentScreen = ref<'start' | 'taking' | 'result' | 'review'>('start')
@ -185,6 +185,13 @@ const submitQuiz = async (auto = false) => {
if (res.success) {
quizResult.value = res.data
// Force mark lesson complete if passed (Fix for checkmark issue)
if (res.data.is_passed) {
markLessonComplete(courseId, lessonId).then(() => {
console.log('Explicitly marked lesson complete')
})
}
} else {
// Fallback error handling
alert(res.error || 'Failed to submit quiz')