feat: Implement course management composable with course fetching and enrollment, and create a user dashboard to display enrolled courses.
This commit is contained in:
parent
eb248f7ca2
commit
f736eb7f38
2 changed files with 33 additions and 3 deletions
|
|
@ -608,6 +608,32 @@ export const useCourse = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ฟังก์ชันดึงรายการใบ Certificate ทั้งหมดของผู้ใช้ (ใหม่)
|
||||||
|
// Endpoint: GET /certificates
|
||||||
|
const fetchAllCertificates = async () => {
|
||||||
|
try {
|
||||||
|
const data = await $fetch<{ code: number; message: string; data: Certificate[] }>(`${API_BASE_URL}/certificates`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: token.value ? {
|
||||||
|
Authorization: `Bearer ${token.value}`
|
||||||
|
} : {}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: data.data || []
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('Fetch all certificates failed:', err)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: err.data?.message || err.message || 'Error fetching certificates',
|
||||||
|
code: err.data?.code,
|
||||||
|
status: err.status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fetchCourses,
|
fetchCourses,
|
||||||
fetchCourseById,
|
fetchCourseById,
|
||||||
|
|
@ -622,6 +648,7 @@ export const useCourse = () => {
|
||||||
submitQuiz,
|
submitQuiz,
|
||||||
generateCertificate,
|
generateCertificate,
|
||||||
getCertificate,
|
getCertificate,
|
||||||
|
fetchAllCertificates,
|
||||||
fetchCourseAnnouncements
|
fetchCourseAnnouncements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ const { currentUser } = useAuth()
|
||||||
const { fetchCourses } = useCourse() // Import useCourse
|
const { fetchCourses } = useCourse() // Import useCourse
|
||||||
const { fetchCategories } = useCategory() // Import useCategory
|
const { fetchCategories } = useCategory() // Import useCategory
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// Helper to get localized text
|
// Helper to get localized text
|
||||||
const getLocalizedText = (text: string | { th: string; en: string } | undefined) => {
|
const getLocalizedText = (text: string | { th: string; en: string } | undefined) => {
|
||||||
if (!text) return ''
|
if (!text) return ''
|
||||||
|
|
@ -50,7 +52,7 @@ onMounted(async () => {
|
||||||
id: c.id,
|
id: c.id,
|
||||||
title: getLocalizedText(c.title),
|
title: getLocalizedText(c.title),
|
||||||
category: getLocalizedText(catMap.get(c.category_id)) || 'General', // Map Category ID to Name
|
category: getLocalizedText(catMap.get(c.category_id)) || 'General', // Map Category ID to Name
|
||||||
duration: c.lessons ? `${c.lessons} บทเรียน` : 'พร้อมเรียน', // Use lesson count or default
|
duration: c.lessons ? `${c.lessons} ${t('course.lessonsUnit')}` : '', // Use lesson count or empty
|
||||||
image: c.thumbnail_url || '',
|
image: c.thumbnail_url || '',
|
||||||
badge: '', // No mock badge
|
badge: '', // No mock badge
|
||||||
badgeType: ''
|
badgeType: ''
|
||||||
|
|
@ -96,11 +98,12 @@ onMounted(async () => {
|
||||||
<div class="p-7" style="background-color: var(--bg-surface);">
|
<div class="p-7" style="background-color: var(--bg-surface);">
|
||||||
<div class="text-[10px] font-black text-slate-700 dark:text-slate-500 uppercase tracking-[0.2em] mb-2">{{ course.category }}</div>
|
<div class="text-[10px] font-black text-slate-700 dark:text-slate-500 uppercase tracking-[0.2em] mb-2">{{ course.category }}</div>
|
||||||
<h4 class="font-black text-xl mb-6 text-slate-900 dark:text-white group-hover:text-blue-700 dark:group-hover:text-blue-400 transition-colors line-clamp-2 h-14">{{ course.title }}</h4>
|
<h4 class="font-black text-xl mb-6 text-slate-900 dark:text-white group-hover:text-blue-700 dark:group-hover:text-blue-400 transition-colors line-clamp-2 h-14">{{ course.title }}</h4>
|
||||||
<div class="flex items-center justify-between pt-4 border-t border-slate-50 dark:border-white/5">
|
<div class="flex items-center justify-between pt-4 border-t border-slate-50 dark:border-white/5">
|
||||||
<span class="text-xs font-bold text-slate-700 dark:text-slate-400 flex items-center gap-2">
|
<span v-if="course.duration" class="text-xs font-bold text-slate-700 dark:text-slate-400 flex items-center gap-2">
|
||||||
<q-icon name="schedule" size="14px" />
|
<q-icon name="schedule" size="14px" />
|
||||||
{{ course.duration }}
|
{{ course.duration }}
|
||||||
</span>
|
</span>
|
||||||
|
<div v-else />
|
||||||
<span class="px-4 py-2 bg-blue-600 text-white text-xs font-bold rounded-lg hover:bg-blue-700 transition-colors shadow-md">{{ $t('menu.viewDetails') }}</span>
|
<span class="px-4 py-2 bg-blue-600 text-white text-xs font-bold rounded-lg hover:bg-blue-700 transition-colors shadow-md">{{ $t('menu.viewDetails') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue